Merge "Unmute audio streams for full volume device"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 2fd2e33..9b290c6 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -12,8 +12,6 @@
services/incremental/
[Hook Scripts]
-checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
-
strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT}
hidden_api_txt_checksorted_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
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..6a044508 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();
@@ -3673,8 +3674,13 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void grantPermission(android.hardware.usb.UsbDevice, String);
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void resetUsbGadget();
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long);
+ field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_ACCESSORY_HANDSHAKE = "android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE";
field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE";
+ field public static final String EXTRA_ACCESSORY_HANDSHAKE_END = "android.hardware.usb.extra.ACCESSORY_HANDSHAKE_END";
+ field public static final String EXTRA_ACCESSORY_START = "android.hardware.usb.extra.ACCESSORY_START";
+ field public static final String EXTRA_ACCESSORY_STRING_COUNT = "android.hardware.usb.extra.ACCESSORY_STRING_COUNT";
+ field public static final String EXTRA_ACCESSORY_UEVENT_TIME = "android.hardware.usb.extra.ACCESSORY_UEVENT_TIME";
field public static final long FUNCTION_ACCESSORY = 2L; // 0x2L
field public static final long FUNCTION_ADB = 1L; // 0x1L
field public static final long FUNCTION_AUDIO_SOURCE = 64L; // 0x40L
diff --git a/api/test-current.txt b/api/test-current.txt
index 113d585..ab07613 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -35,6 +35,7 @@
public static final class R.bool {
field public static final int config_assistantOnTopOfDream = 17891333; // 0x1110005
field public static final int config_perDisplayFocusEnabled = 17891332; // 0x1110004
+ field public static final int config_remoteInsetsControllerControlsSystemBars = 17891334; // 0x1110006
}
public static final class R.string {
@@ -144,6 +145,7 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void stopSystemLockTaskMode();
method public static boolean supportsMultiWindow(android.content.Context);
method public static boolean supportsSplitScreenMultiWindow(android.content.Context);
+ field public static final int DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP = 440; // 0x1b8
field public static final int INVALID_STACK_ID = -1; // 0xffffffff
field public static final int SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT = 1; // 0x1
field public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0; // 0x0
@@ -5002,7 +5004,6 @@
field public static final String PERSIST_PREFIX = "persist.sys.fflag.override.";
field public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
field public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer";
- field public static final String SETTINGS_FUSE_FLAG = "settings_fuse";
field public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
}
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/Android.bp b/cmds/statsd/Android.bp
index 0617eb6..124f815f 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -216,10 +216,6 @@
// address: true,
//},
},
- debuggable: {
- // Add a flag to enable stats log printing from statsd on debug builds.
- cflags: ["-DVERY_VERBOSE_PRINTING"],
- },
},
proto: {
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index e7b32c5..05e9ec3 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -409,11 +409,9 @@
onWatchdogRollbackOccurredLocked(event);
}
-#ifdef VERY_VERBOSE_PRINTING
if (mPrintAllLogs) {
ALOGI("%s", event->ToString().c_str());
}
-#endif
resetIfConfigTtlExpiredLocked(eventElapsedTimeNs);
// Hard-coded logic to update the isolated uid's in the uid-map.
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 23f2584..c0f54a0 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -139,10 +139,8 @@
int64_t getLastReportTimeNs(const ConfigKey& key);
inline void setPrintLogs(bool enabled) {
-#ifdef VERY_VERBOSE_PRINTING
std::lock_guard<std::mutex> lock(mMetricsMutex);
mPrintAllLogs = enabled;
-#endif
}
// Add a specific config key to the possible configs to dump ASAP.
@@ -276,9 +274,7 @@
//Last time we wrote metadata to disk.
int64_t mLastMetadataWriteNs = 0;
-#ifdef VERY_VERBOSE_PRINTING
bool mPrintAllLogs = false;
-#endif
FRIEND_TEST(StatsLogProcessorTest, TestOutOfOrderLogs);
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 3226482..d5e3314 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -484,7 +484,8 @@
dprintf(out, " Clear cached puller data.\n");
dprintf(out, "\n");
dprintf(out, "usage: adb shell cmd stats print-logs\n");
- dprintf(out, " Only works on eng build\n");
+ dprintf(out, " Requires root privileges.\n");
+ dprintf(out, " Can be disabled by calling adb shell cmd stats print-logs 0\n");
}
status_t StatsService::cmd_trigger_broadcast(int out, Vector<String8>& args) {
@@ -865,18 +866,19 @@
}
status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) {
- VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", AIBinder_getCallingPid(),
- AIBinder_getCallingUid());
- if (checkPermission(kPermissionDump)) {
- bool enabled = true;
- if (args.size() >= 2) {
- enabled = atoi(args[1].c_str()) != 0;
- }
- mProcessor->setPrintLogs(enabled);
- return NO_ERROR;
- } else {
+ Status status = checkUid(AID_ROOT);
+ if (!status.isOk()) {
return PERMISSION_DENIED;
}
+
+ VLOG("StatsService::cmd_print_logs with pid %i, uid %i", AIBinder_getCallingPid(),
+ AIBinder_getCallingUid());
+ bool enabled = true;
+ if (args.size() >= 2) {
+ enabled = atoi(args[1].c_str()) != 0;
+ }
+ mProcessor->setPrintLogs(enabled);
+ return NO_ERROR;
}
bool StatsService::getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid) {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index b50af38..02c0d93 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 [(log_mode) = MODE_BYTES];
+}
+
/**
* Logs when MediaProvider has successfully finished scanning a storage volume.
*
@@ -6147,7 +6174,7 @@
* Pulled from ProcessStatsService.java
*/
message ProcStats {
- optional ProcessStatsSectionProto proc_stats_section = 1;
+ optional ProcessStatsSectionProto proc_stats_section = 1 [(log_mode) = MODE_BYTES];
// Data pulled from device into this is sometimes sharded across multiple atoms to work around
// a size limit. When this happens, this shard ID will contain an increasing 1-indexed integer
// with the number of this shard.
@@ -6158,7 +6185,7 @@
* Pulled from ProcessStatsService.java
*/
message ProcStatsPkgProc {
- optional ProcessStatsSectionProto proc_stats_section = 1;
+ optional ProcessStatsSectionProto proc_stats_section = 1 [(log_mode) = MODE_BYTES];
}
// Next Tag: 2
@@ -6176,7 +6203,7 @@
* Pulled from NotificationManagerService.java
*/
message NotificationRemoteViews {
- optional NotificationRemoteViewsProto notification_remote_views = 1;
+ optional NotificationRemoteViewsProto notification_remote_views = 1 [(log_mode) = MODE_BYTES];
}
/**
@@ -6244,7 +6271,7 @@
// May also be "MANUAL_RULE" to indicate app-activation of the manual rule.
optional string id = 5;
optional int32 uid = 6 [(is_uid) = true]; // currently only SYSTEM_UID or 0 for other
- optional DNDPolicyProto policy = 7;
+ optional DNDPolicyProto policy = 7 [(log_mode) = MODE_BYTES];
}
/**
@@ -6409,7 +6436,7 @@
* Pulled from PowerProfile.java
*/
message PowerProfile {
- optional PowerProfileProto power_profile = 1;
+ optional PowerProfileProto power_profile = 1 [(log_mode) = MODE_BYTES];
}
/**
@@ -8104,7 +8131,7 @@
message TrainInfo {
optional int64 train_version_code = 1;
- optional TrainExperimentIds train_experiment_id = 2;
+ optional TrainExperimentIds train_experiment_id = 2 [(log_mode) = MODE_BYTES];
optional string train_name = 3;
@@ -11152,8 +11179,8 @@
optional int64 expiry_timestamp_millis = 3;
// List of committers of this Blob
- optional BlobCommitterListProto committers = 4;
+ optional BlobCommitterListProto committers = 4 [(log_mode) = MODE_BYTES];
// List of leasees of this Blob
- optional BlobLeaseeListProto leasees = 5;
+ optional BlobLeaseeListProto leasees = 5 [(log_mode) = MODE_BYTES];
}
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 8a9ec74..46c3770 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -92,63 +92,43 @@
}
bool StatsPullerManager::Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
- vector<shared_ptr<LogEvent>>* data, bool useUids) {
+ vector<shared_ptr<LogEvent>>* data) {
std::lock_guard<std::mutex> _l(mLock);
- return PullLocked(tagId, configKey, eventTimeNs, data, useUids);
+ return PullLocked(tagId, configKey, eventTimeNs, data);
}
bool StatsPullerManager::Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids) {
+ vector<std::shared_ptr<LogEvent>>* data) {
std::lock_guard<std::mutex> _l(mLock);
- return PullLocked(tagId, uids, eventTimeNs, data, useUids);
+ return PullLocked(tagId, uids, eventTimeNs, data);
}
bool StatsPullerManager::PullLocked(int tagId, const ConfigKey& configKey,
- const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data,
- bool useUids) {
+ const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data) {
vector<int32_t> uids;
- if (useUids) {
- auto uidProviderIt = mPullUidProviders.find(configKey);
- if (uidProviderIt == mPullUidProviders.end()) {
- ALOGE("Error pulling tag %d. No pull uid provider for config key %s", tagId,
- configKey.ToString().c_str());
- StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
- return false;
- }
- sp<PullUidProvider> pullUidProvider = uidProviderIt->second.promote();
- if (pullUidProvider == nullptr) {
- ALOGE("Error pulling tag %d, pull uid provider for config %s is gone.", tagId,
- configKey.ToString().c_str());
- StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
- return false;
- }
- uids = pullUidProvider->getPullAtomUids(tagId);
+ const auto& uidProviderIt = mPullUidProviders.find(configKey);
+ if (uidProviderIt == mPullUidProviders.end()) {
+ ALOGE("Error pulling tag %d. No pull uid provider for config key %s", tagId,
+ configKey.ToString().c_str());
+ StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
+ return false;
}
- return PullLocked(tagId, uids, eventTimeNs, data, useUids);
+ sp<PullUidProvider> pullUidProvider = uidProviderIt->second.promote();
+ if (pullUidProvider == nullptr) {
+ ALOGE("Error pulling tag %d, pull uid provider for config %s is gone.", tagId,
+ configKey.ToString().c_str());
+ StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
+ return false;
+ }
+ uids = pullUidProvider->getPullAtomUids(tagId);
+ return PullLocked(tagId, uids, eventTimeNs, data);
}
bool StatsPullerManager::PullLocked(int tagId, const vector<int32_t>& uids,
- const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data,
- bool useUids) {
+ const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data) {
VLOG("Initiating pulling %d", tagId);
- if (useUids) {
- for (int32_t uid : uids) {
- PullerKey key = {.atomTag = tagId, .uid = uid};
- auto pullerIt = kAllPullAtomInfo.find(key);
- if (pullerIt != kAllPullAtomInfo.end()) {
- bool ret = pullerIt->second->Pull(eventTimeNs, data);
- VLOG("pulled %zu items", data->size());
- if (!ret) {
- StatsdStats::getInstance().notePullFailed(tagId);
- }
- return ret;
- }
- }
- StatsdStats::getInstance().notePullerNotFound(tagId);
- ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
- return false; // Return early since we don't know what to pull.
- } else {
- PullerKey key = {.atomTag = tagId, .uid = -1};
+ for (int32_t uid : uids) {
+ PullerKey key = {.atomTag = tagId, .uid = uid};
auto pullerIt = kAllPullAtomInfo.find(key);
if (pullerIt != kAllPullAtomInfo.end()) {
bool ret = pullerIt->second->Pull(eventTimeNs, data);
@@ -158,9 +138,10 @@
}
return ret;
}
- ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
- return false; // Return early since we don't know what to pull.
}
+ StatsdStats::getInstance().notePullerNotFound(tagId);
+ ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
+ return false; // Return early since we don't know what to pull.
}
bool StatsPullerManager::PullerForMatcherExists(int tagId) const {
@@ -352,8 +333,7 @@
void StatsPullerManager::RegisterPullAtomCallback(const int uid, const int32_t atomTag,
const int64_t coolDownNs, const int64_t timeoutNs,
const vector<int32_t>& additiveFields,
- const shared_ptr<IPullAtomCallback>& callback,
- bool useUid) {
+ const shared_ptr<IPullAtomCallback>& callback) {
std::lock_guard<std::mutex> _l(mLock);
VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
@@ -368,16 +348,15 @@
sp<StatsCallbackPuller> puller = new StatsCallbackPuller(atomTag, callback, actualCoolDownNs,
actualTimeoutNs, additiveFields);
- PullerKey key = {.atomTag = atomTag, .uid = useUid ? uid : -1};
+ PullerKey key = {.atomTag = atomTag, .uid = uid};
AIBinder_linkToDeath(callback->asBinder().get(), mPullAtomCallbackDeathRecipient.get(),
new PullAtomCallbackDeathCookie(this, key, puller));
kAllPullAtomInfo[key] = puller;
}
-void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag,
- bool useUids) {
+void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag) {
std::lock_guard<std::mutex> _l(mLock);
- PullerKey key = {.atomTag = atomTag, .uid = useUids ? uid : -1};
+ PullerKey key = {.atomTag = atomTag, .uid = uid};
if (kAllPullAtomInfo.find(key) != kAllPullAtomInfo.end()) {
StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag,
/*registered=*/false);
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 194a0f5..489cbdb 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -102,11 +102,11 @@
// If the metric wants to make any change to the data, like timestamps, they
// should make a copy as this data may be shared with multiple metrics.
virtual bool Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
+ vector<std::shared_ptr<LogEvent>>* data);
// Same as above, but directly specify the allowed uids to pull from.
virtual bool Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
+ vector<std::shared_ptr<LogEvent>>* data);
// Clear pull data cache immediately.
int ForceClearPullerCache();
@@ -118,10 +118,9 @@
void RegisterPullAtomCallback(const int uid, const int32_t atomTag, const int64_t coolDownNs,
const int64_t timeoutNs, const vector<int32_t>& additiveFields,
- const shared_ptr<IPullAtomCallback>& callback,
- bool useUid = true);
+ const shared_ptr<IPullAtomCallback>& callback);
- void UnregisterPullAtomCallback(const int uid, const int32_t atomTag, bool useUids = true);
+ void UnregisterPullAtomCallback(const int uid, const int32_t atomTag);
std::map<const PullerKey, sp<StatsPuller>> kAllPullAtomInfo;
@@ -153,10 +152,10 @@
std::map<ConfigKey, wp<PullUidProvider>> mPullUidProviders;
bool PullLocked(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
+ vector<std::shared_ptr<LogEvent>>* data);
bool PullLocked(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids);
+ vector<std::shared_ptr<LogEvent>>* data);
// locks for data receiver and StatsCompanionService changes
std::mutex mLock;
diff --git a/cmds/statsd/tests/external/StatsPullerManager_test.cpp b/cmds/statsd/tests/external/StatsPullerManager_test.cpp
index c76e85e..0d539f4 100644
--- a/cmds/statsd/tests/external/StatsPullerManager_test.cpp
+++ b/cmds/statsd/tests/external/StatsPullerManager_test.cpp
@@ -89,10 +89,10 @@
sp<StatsPullerManager> createPullerManagerAndRegister() {
sp<StatsPullerManager> pullerManager = new StatsPullerManager();
shared_ptr<FakePullAtomCallback> cb1 = SharedRefBase::make<FakePullAtomCallback>(uid1);
- pullerManager->RegisterPullAtomCallback(uid1, pullTagId1, coolDownNs, timeoutNs, {}, cb1, true);
+ pullerManager->RegisterPullAtomCallback(uid1, pullTagId1, coolDownNs, timeoutNs, {}, cb1);
shared_ptr<FakePullAtomCallback> cb2 = SharedRefBase::make<FakePullAtomCallback>(uid2);
- pullerManager->RegisterPullAtomCallback(uid2, pullTagId1, coolDownNs, timeoutNs, {}, cb2, true);
- pullerManager->RegisterPullAtomCallback(uid1, pullTagId2, coolDownNs, timeoutNs, {}, cb1, true);
+ pullerManager->RegisterPullAtomCallback(uid2, pullTagId1, coolDownNs, timeoutNs, {}, cb2);
+ pullerManager->RegisterPullAtomCallback(uid1, pullTagId2, coolDownNs, timeoutNs, {}, cb1);
return pullerManager;
}
} // anonymous namespace
@@ -101,14 +101,14 @@
sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
vector<shared_ptr<LogEvent>> data;
- EXPECT_FALSE(pullerManager->Pull(pullTagId1, {unregisteredUid}, /*timestamp =*/1, &data, true));
+ EXPECT_FALSE(pullerManager->Pull(pullTagId1, {unregisteredUid}, /*timestamp =*/1, &data));
}
TEST(StatsPullerManagerTest, TestPullChoosesCorrectUid) {
sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
vector<shared_ptr<LogEvent>> data;
- EXPECT_TRUE(pullerManager->Pull(pullTagId1, {uid1}, /*timestamp =*/1, &data, true));
+ EXPECT_TRUE(pullerManager->Pull(pullTagId1, {uid1}, /*timestamp =*/1, &data));
ASSERT_EQ(data.size(), 1);
EXPECT_EQ(data[0]->GetTagId(), pullTagId1);
ASSERT_EQ(data[0]->getValues().size(), 1);
@@ -121,7 +121,7 @@
pullerManager->RegisterPullUidProvider(configKey, uidProvider);
vector<shared_ptr<LogEvent>> data;
- EXPECT_FALSE(pullerManager->Pull(pullTagId1, badConfigKey, /*timestamp =*/1, &data, true));
+ EXPECT_FALSE(pullerManager->Pull(pullTagId1, badConfigKey, /*timestamp =*/1, &data));
}
TEST(StatsPullerManagerTest, TestPullConfigKeyGood) {
@@ -130,7 +130,7 @@
pullerManager->RegisterPullUidProvider(configKey, uidProvider);
vector<shared_ptr<LogEvent>> data;
- EXPECT_TRUE(pullerManager->Pull(pullTagId1, configKey, /*timestamp =*/1, &data, true));
+ EXPECT_TRUE(pullerManager->Pull(pullTagId1, configKey, /*timestamp =*/1, &data));
EXPECT_EQ(data[0]->GetTagId(), pullTagId1);
ASSERT_EQ(data[0]->getValues().size(), 1);
EXPECT_EQ(data[0]->getValues()[0].mValue.int_value, uid2);
@@ -142,7 +142,7 @@
pullerManager->RegisterPullUidProvider(configKey, uidProvider);
vector<shared_ptr<LogEvent>> data;
- EXPECT_FALSE(pullerManager->Pull(pullTagId2, configKey, /*timestamp =*/1, &data, true));
+ EXPECT_FALSE(pullerManager->Pull(pullTagId2, configKey, /*timestamp =*/1, &data));
}
} // namespace statsd
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 5997bed..caea42d 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -137,9 +137,9 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(makeLogEvent(tagId, eventTimeNs + 10, 3, "some value", 11));
@@ -310,10 +310,10 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Return(false))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 2));
@@ -388,7 +388,7 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Return(false));
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
@@ -440,9 +440,9 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, conditionChangeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, conditionChangeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs + 10, 100));
return true;
@@ -527,9 +527,9 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, sliceConditionChangeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, sliceConditionChangeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs + 10, 1000, 100));
return true;
@@ -566,7 +566,7 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Return(false));
GaugeMetric metric;
@@ -665,16 +665,16 @@
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 4));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5));
@@ -737,23 +737,23 @@
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 3, 4));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 5));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 6));
@@ -815,10 +815,10 @@
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 3, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 3, _))
// Bucket start.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 10));
return true;
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 5666501..97757af 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -294,9 +294,9 @@
TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
return true;
@@ -368,19 +368,19 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2;
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Initialize bucket.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
return true;
}))
// Partial bucket.
- .WillOnce(Invoke([partialBucketSplitTimeNs](
- int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
+ const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
data->push_back(
@@ -434,9 +434,9 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 3, 3));
return true;
@@ -505,7 +505,7 @@
metric.set_use_absolute_value_on_reset(true);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Return(true));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -565,7 +565,7 @@
TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Return(false));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -621,23 +621,23 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // First condition change.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1); // Second condition change.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 130));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 1); // Third condition change.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 180));
@@ -770,11 +770,11 @@
int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 150;
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Return(true))
- .WillOnce(Invoke([partialBucketSplitTimeNs](
- int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
+ const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 120));
@@ -830,7 +830,7 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Return(true));
ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
@@ -854,16 +854,16 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to true time.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs,
bucket2StartTimeNs - 100); // Condition change to false time.
data->clear();
@@ -1085,7 +1085,7 @@
TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Return(true));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -1164,10 +1164,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// condition becomes true
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // First condition change.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
@@ -1175,7 +1175,7 @@
}))
// condition becomes false
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1); // Second condition change.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120));
@@ -1227,10 +1227,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// condition becomes true
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
@@ -1238,7 +1238,7 @@
}))
// condition becomes false
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120));
@@ -1246,7 +1246,7 @@
}))
// condition becomes true again
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 25);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 25, 130));
@@ -1677,9 +1677,9 @@
metric.set_use_zero_default_base(true);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
return true;
@@ -1753,9 +1753,9 @@
metric.set_use_zero_default_base(true);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
return true;
@@ -1858,9 +1858,9 @@
metric.mutable_dimensions_in_what()->add_child()->set_field(1);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
return true;
@@ -1961,9 +1961,9 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// Used by onConditionChanged.
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
return true;
@@ -1995,9 +1995,9 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // Condition change to true.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
@@ -2034,16 +2034,16 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 50));
return false;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to false.
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
@@ -2077,9 +2077,9 @@
metric.set_max_pull_delay_sec(0);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 120));
return true;
@@ -2124,9 +2124,9 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100));
return true;
@@ -2156,12 +2156,12 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First onConditionChanged
.WillOnce(Return(false))
// Second onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
@@ -2233,10 +2233,10 @@
metric.set_condition(StringToId("SCREEN_ON"));
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 2, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 2, _))
// First onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
for (int i = 0; i < 2000; i++) {
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, i));
}
@@ -2290,10 +2290,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 2);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 120));
@@ -2301,7 +2301,7 @@
}))
// Second onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
@@ -2369,10 +2369,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 2);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 120));
@@ -2380,7 +2380,7 @@
}))
// Second onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
@@ -2442,10 +2442,10 @@
TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
// Start bucket.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
return true;
@@ -2475,17 +2475,17 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
return true;
@@ -2518,24 +2518,24 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 11);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 2));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 12);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 5));
@@ -2582,10 +2582,10 @@
metric.set_condition(StringToId("SCREEN_ON"));
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _))
// First onConditionChanged
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
@@ -2625,19 +2625,19 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
int64_t partialBucketSplitTimeNs = bucketStartTimeNs + bucketSizeNs / 2;
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Initialization.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
}))
// notifyAppUpgrade.
- .WillOnce(Invoke([partialBucketSplitTimeNs](
- int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
+ const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10));
@@ -2681,10 +2681,10 @@
TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Second onConditionChanged.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 5));
@@ -2692,7 +2692,7 @@
}))
// Third onConditionChanged.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 10, 7));
@@ -2752,10 +2752,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
// Initialization.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
@@ -2782,19 +2782,19 @@
int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2;
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Initialization.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
}))
// notifyAppUpgrade.
- .WillOnce(Invoke([partialBucketSplitTimeNs](
- int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
+ const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10));
@@ -2822,10 +2822,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First on condition changed.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
@@ -2833,7 +2833,7 @@
}))
// Second on condition changed.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
@@ -2867,10 +2867,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First condition change.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
@@ -2878,7 +2878,7 @@
}))
// 2nd condition change.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 8);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1));
@@ -2886,7 +2886,7 @@
}))
// 3rd condition change.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1));
@@ -2931,10 +2931,10 @@
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
// Initial pull.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
return true;
@@ -2969,10 +2969,10 @@
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
// Initial pull.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
return true;
@@ -3013,17 +3013,17 @@
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Initial pull.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
return true;
}))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(
@@ -3069,10 +3069,10 @@
metric.set_use_diff(false);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// condition becomes true
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
@@ -3080,7 +3080,7 @@
}))
// condition becomes false
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 20));
@@ -3119,10 +3119,10 @@
metric.set_use_diff(false);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _))
// condition becomes true
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
return true;
@@ -3170,10 +3170,10 @@
metric.set_use_diff(false);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// condition becomes true
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
@@ -3210,10 +3210,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 20, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 20, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20, 10));
return true;
@@ -3256,10 +3256,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 50, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 50, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
return true;
@@ -3314,10 +3314,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
@@ -3325,7 +3325,7 @@
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 100);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 100, 15));
@@ -3380,10 +3380,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
@@ -3391,7 +3391,7 @@
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10000);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 100, 15));
@@ -3436,10 +3436,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
@@ -3486,10 +3486,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
@@ -3497,7 +3497,7 @@
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket4StartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1000, 15));
@@ -3560,10 +3560,10 @@
metric.set_min_bucket_size_nanos(10000000000); // 10 seconds
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
@@ -3571,7 +3571,7 @@
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 9000000);
data->clear();
data->push_back(
@@ -3651,10 +3651,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(
@@ -3663,7 +3663,7 @@
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 15 * NS_PER_SEC);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(
@@ -3740,10 +3740,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
@@ -3751,7 +3751,7 @@
}))
// App Update.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1000);
data->clear();
data->push_back(
@@ -3806,10 +3806,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _))
// Condition change to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
return true;
@@ -3857,10 +3857,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// First condition change event.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
for (int i = 0; i < 2000; i++) {
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, i));
@@ -3877,7 +3877,7 @@
.WillOnce(Return(false))
.WillOnce(Return(false))
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 220);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 220, 10));
@@ -3976,10 +3976,10 @@
// Set up ValueMetricProducer.
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE");
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// ValueMetricProducer initialized.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
@@ -3987,7 +3987,7 @@
}))
// Screen state change to ON.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5));
@@ -3995,7 +3995,7 @@
}))
// Screen state change to OFF.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 9));
@@ -4003,7 +4003,7 @@
}))
// Screen state change to ON.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21));
@@ -4011,7 +4011,7 @@
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30));
@@ -4178,10 +4178,10 @@
// Set up ValueMetricProducer.
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE_ONOFF");
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// ValueMetricProducer initialized.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
@@ -4189,7 +4189,7 @@
}))
// Screen state change to ON.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5));
@@ -4203,7 +4203,7 @@
// Screen state change to OFF.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21));
@@ -4211,7 +4211,7 @@
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30));
@@ -4408,10 +4408,10 @@
*fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// ValueMetricProducer initialized.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 2 /*uid*/, 7));
@@ -4420,7 +4420,7 @@
}))
// Uid 1 process state change from kStateUnknown -> Foreground
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
data->clear();
data->push_back(
@@ -4433,7 +4433,7 @@
}))
// Uid 2 process state change from kStateUnknown -> Background
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40);
data->clear();
data->push_back(
@@ -4446,7 +4446,7 @@
}))
// Uid 1 process state change from Foreground -> Background
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 20);
data->clear();
data->push_back(
@@ -4459,7 +4459,7 @@
}))
// Uid 1 process state change from Background -> Foreground
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 40);
data->clear();
data->push_back(
@@ -4472,7 +4472,7 @@
}))
// Dump report pull.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50);
data->clear();
data->push_back(
@@ -4852,10 +4852,10 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithConditionAndState(
"BATTERY_SAVER_MODE_STATE");
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// Condition changed to true.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
data->clear();
data->push_back(
@@ -4864,7 +4864,7 @@
}))
// Battery saver mode state changed to OFF.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
data->clear();
data->push_back(
@@ -4873,7 +4873,7 @@
}))
// Condition changed to false.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC);
data->clear();
data->push_back(CreateRepeatedValueLogEvent(
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index eeb38a4..39232c1 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -38,11 +38,10 @@
int64_t nextPulltimeNs, int64_t intervalNs));
MOCK_METHOD3(UnRegisterReceiver,
void(int tagId, const ConfigKey& key, wp<PullDataReceiver> receiver));
- MOCK_METHOD5(Pull, bool(const int pullCode, const ConfigKey& key, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids));
- MOCK_METHOD5(Pull,
- bool(const int pullCode, const vector<int32_t>& uids, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data, bool useUids));
+ MOCK_METHOD4(Pull, bool(const int pullCode, const ConfigKey& key, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data));
+ MOCK_METHOD4(Pull, bool(const int pullCode, const vector<int32_t>& uids,
+ const int64_t eventTimeNs, vector<std::shared_ptr<LogEvent>>* data));
MOCK_METHOD2(RegisterPullUidProvider,
void(const ConfigKey& configKey, wp<PullUidProvider> provider));
MOCK_METHOD2(UnregisterPullUidProvider,
diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
index e384b6a..4fa4135 100644
--- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -190,9 +190,9 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
const vector<int32_t> uids = {AID_SYSTEM};
- EXPECT_CALL(*pullerManager, Pull(10016, uids, _, _, _))
+ EXPECT_CALL(*pullerManager, Pull(10016, uids, _, _))
.WillRepeatedly(Invoke([](int tagId, const vector<int32_t>&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data, bool) {
+ vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid1, /*timeMillis=*/kCpuTime1));
data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid2, /*timeMillis=*/kCpuTime2));
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/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 0f31529..4283d7a 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -31,6 +31,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.DisplayMetrics;
import android.util.Singleton;
import java.util.List;
@@ -139,6 +140,8 @@
public static final String EXTRA_IGNORE_TARGET_SECURITY =
"android.app.extra.EXTRA_IGNORE_TARGET_SECURITY";
+ /** The minimal size of a display's long-edge needed to support split-screen multi-window. */
+ public static final int DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP = 440;
private static int sMaxRecentTasks = -1;
@@ -282,8 +285,23 @@
com.android.internal.R.bool.config_supportsMultiWindow);
}
- /** Returns true if the system supports split screen multi-window. */
+ /**
+ * Returns {@code true} if the display the context is associated with supports split screen
+ * multi-window.
+ *
+ * @throws UnsupportedOperationException if the supplied {@link Context} is not associated with
+ * a display.
+ */
public static boolean supportsSplitScreenMultiWindow(Context context) {
+ DisplayMetrics dm = new DisplayMetrics();
+ context.getDisplay().getRealMetrics(dm);
+
+ int widthDp = (int) (dm.widthPixels / dm.density);
+ int heightDp = (int) (dm.heightPixels / dm.density);
+ if (Math.max(widthDp, heightDp) < DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP) {
+ return false;
+ }
+
return supportsMultiWindow(context)
&& Resources.getSystem().getBoolean(
com.android.internal.R.bool.config_supportsSplitScreenMultiWindow);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 89e1f5a..9b13d25 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -86,6 +86,8 @@
import android.graphics.HardwareRenderer;
import android.hardware.display.DisplayManagerGlobal;
import android.inputmethodservice.InputMethodService;
+import android.media.MediaFrameworkInitializer;
+import android.media.MediaServiceManager;
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
import android.net.Proxy;
@@ -269,9 +271,6 @@
/** Type for IActivityManager.serviceDoneExecuting: done stopping (destroying) service */
public static final int SERVICE_DONE_EXECUTING_STOP = 2;
- // Whether to invoke an activity callback after delivering new configuration.
- private static final boolean REPORT_TO_ACTIVITY = true;
-
/** Use foreground GC policy (less pause time) and higher JIT weight. */
private static final int VM_PROCESS_STATE_JANK_PERCEPTIBLE = 0;
/** Use background GC policy and default JIT threshold. */
@@ -4971,7 +4970,8 @@
private void relaunchAllActivities(boolean preserveWindows) {
for (Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
final ActivityClientRecord r = entry.getValue();
- if (!r.activity.mFinished) {
+ // Schedule relaunch the activity if it is not a local object or finishing.
+ if (!r.activity.mFinished && !(r.token instanceof Binder)) {
if (preserveWindows && r.window != null) {
r.mPreserveWindow = true;
}
@@ -5537,18 +5537,14 @@
}
/**
- * Updates the configuration for an Activity. The ActivityClientRecord's
- * {@link ActivityClientRecord#overrideConfig} is used to compute the final Configuration for
- * that Activity. {@link ActivityClientRecord#tmpConfig} is used as a temporary for delivering
- * the updated Configuration.
- * @param r ActivityClientRecord representing the Activity.
- * @param newBaseConfig The new configuration to use. This may be augmented with
- * {@link ActivityClientRecord#overrideConfig}.
+ * Updates the configuration for an Activity in its current display.
+ *
+ * @see #performConfigurationChangedForActivity(ActivityClientRecord, Configuration, int,
+ * boolean)
*/
private void performConfigurationChangedForActivity(ActivityClientRecord r,
Configuration newBaseConfig) {
- performConfigurationChangedForActivity(r, newBaseConfig, r.activity.getDisplayId(),
- false /* movedToDifferentDisplay */);
+ performConfigurationChangedForActivity(r, newBaseConfig, r.activity.getDisplayId());
}
/**
@@ -5560,17 +5556,16 @@
* @param newBaseConfig The new configuration to use. This may be augmented with
* {@link ActivityClientRecord#overrideConfig}.
* @param displayId The id of the display where the Activity currently resides.
- * @param movedToDifferentDisplay Indicates if the activity was moved to different display.
* @return {@link Configuration} instance sent to client, null if not sent.
*/
private Configuration performConfigurationChangedForActivity(ActivityClientRecord r,
- Configuration newBaseConfig, int displayId, boolean movedToDifferentDisplay) {
+ Configuration newBaseConfig, int displayId) {
r.tmpConfig.setTo(newBaseConfig);
if (r.overrideConfig != null) {
r.tmpConfig.updateFrom(r.overrideConfig);
}
final Configuration reportedConfig = performActivityConfigurationChanged(r.activity,
- r.tmpConfig, r.overrideConfig, displayId, movedToDifferentDisplay);
+ r.tmpConfig, r.overrideConfig, displayId);
freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));
return reportedConfig;
}
@@ -5597,10 +5592,6 @@
* @param newConfig The new configuration.
*/
private void performConfigurationChanged(ComponentCallbacks2 cb, Configuration newConfig) {
- if (!REPORT_TO_ACTIVITY) {
- return;
- }
-
// ContextThemeWrappers may override the configuration for that context. We must check and
// apply any overrides defined.
Configuration contextThemeWrapperOverrideConfig = null;
@@ -5625,12 +5616,10 @@
* from the base global configuration. This is supplied by
* ActivityManager.
* @param displayId Id of the display where activity currently resides.
- * @param movedToDifferentDisplay Indicates if the activity was moved to different display.
* @return Configuration sent to client, null if no changes and not moved to different display.
*/
private Configuration performActivityConfigurationChanged(Activity activity,
- Configuration newConfig, Configuration amOverrideConfig, int displayId,
- boolean movedToDifferentDisplay) {
+ Configuration newConfig, Configuration amOverrideConfig, int displayId) {
if (activity == null) {
throw new IllegalArgumentException("No activity provided.");
}
@@ -5643,6 +5632,7 @@
// callback, see also PinnedStackTests#testConfigurationChangeOrderDuringTransition
handleWindowingModeChangeIfNeeded(activity, newConfig);
+ final boolean movedToDifferentDisplay = isDifferentDisplay(activity, displayId);
boolean shouldReportChange = false;
if (activity.mCurrentConfig == null) {
shouldReportChange = true;
@@ -5656,8 +5646,7 @@
amOverrideConfig)) {
// Nothing significant, don't proceed with updating and reporting.
return null;
- } else if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0
- || !REPORT_TO_ACTIVITY) {
+ } else if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) {
// If this activity doesn't handle any of the config changes, then don't bother
// calling onConfigurationChanged. Otherwise, report to the activity for the
// changes.
@@ -5691,11 +5680,6 @@
final Configuration configToReport = createNewConfigAndUpdateIfNotNull(newConfig,
contextThemeWrapperOverrideConfig);
- if (!REPORT_TO_ACTIVITY) {
- // Not configured to report to activity.
- return configToReport;
- }
-
if (movedToDifferentDisplay) {
activity.dispatchMovedToDisplay(displayId, configToReport);
}
@@ -5988,8 +5972,6 @@
if (DEBUG_CONFIGURATION) Slog.w(TAG, "Not found target activity to report to: " + r);
return;
}
- final boolean movedToDifferentDisplay = displayId != INVALID_DISPLAY
- && displayId != r.activity.getDisplayId();
synchronized (r) {
if (overrideConfig.isOtherSeqNewer(r.mPendingOverrideConfig)) {
@@ -6003,6 +5985,7 @@
r.mPendingOverrideConfig = null;
}
+ final boolean movedToDifferentDisplay = isDifferentDisplay(r.activity, displayId);
if (r.overrideConfig != null && !r.overrideConfig.isOtherSeqNewer(overrideConfig)
&& !movedToDifferentDisplay) {
if (DEBUG_CONFIGURATION) {
@@ -6018,29 +6001,34 @@
final ViewRootImpl viewRoot = r.activity.mDecor != null
? r.activity.mDecor.getViewRootImpl() : null;
- if (movedToDifferentDisplay) {
- if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity moved to display, activity:"
- + r.activityInfo.name + ", displayId=" + displayId
+ if (DEBUG_CONFIGURATION) {
+ Slog.v(TAG, "Handle activity config changed, activity:"
+ + r.activityInfo.name + ", displayId=" + r.activity.getDisplayId()
+ + (movedToDifferentDisplay ? (", newDisplayId=" + displayId) : "")
+ ", config=" + overrideConfig);
-
- final Configuration reportedConfig = performConfigurationChangedForActivity(r,
- mCompatConfiguration, displayId, true /* movedToDifferentDisplay */);
- if (viewRoot != null) {
- viewRoot.onMovedToDisplay(displayId, reportedConfig);
- }
- } else {
- if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity config changed: "
- + r.activityInfo.name + ", config=" + overrideConfig);
- performConfigurationChangedForActivity(r, mCompatConfiguration);
}
+ final Configuration reportedConfig = performConfigurationChangedForActivity(r,
+ mCompatConfiguration,
+ movedToDifferentDisplay ? displayId : r.activity.getDisplayId());
// Notify the ViewRootImpl instance about configuration changes. It may have initiated this
// update to make sure that resources are updated before updating itself.
if (viewRoot != null) {
+ if (movedToDifferentDisplay) {
+ viewRoot.onMovedToDisplay(displayId, reportedConfig);
+ }
viewRoot.updateConfiguration(displayId);
}
mSomeActivitiesChanged = true;
}
+ /**
+ * Checks if the display id of activity is different from the given one. Note that
+ * {@link #INVALID_DISPLAY} means no difference.
+ */
+ private static boolean isDifferentDisplay(@NonNull Activity activity, int displayId) {
+ return displayId != INVALID_DISPLAY && displayId != activity.getDisplayId();
+ }
+
final void handleProfilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) {
if (start) {
try {
@@ -7665,6 +7653,7 @@
public static void initializeMainlineModules() {
TelephonyFrameworkInitializer.setTelephonyServiceManager(new TelephonyServiceManager());
StatsFrameworkInitializer.setStatsServiceManager(new StatsServiceManager());
+ MediaFrameworkInitializer.setMediaServiceManager(new MediaServiceManager());
}
private void purgePendingResources() {
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index cfe0aff..e7b3e14 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -502,7 +502,7 @@
* Return the defining kernel user identifier, maybe different from {@link #getRealUid} and
* {@link #getPackageUid}, if an external service has the
* {@link android.R.styleable#AndroidManifestService_useAppZygote android:useAppZygote} set
- * to <code>true<code> and was bound with the flag
+ * to <code>true</code> and was bound with the flag
* {@link android.content.Context#BIND_EXTERNAL_SERVICE} - in this case, this field here will
* be the kernel user identifier of the external service provider.
*/
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index adf5fa5..9613e58 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1900,24 +1900,31 @@
@Override
public Object getSystemService(String name) {
- // Check incorrect Context usage.
- if (isUiComponent(name) && !isUiContext() && vmIncorrectContextUseEnabled()) {
- final String errorMessage = "Tried to access visual service "
- + SystemServiceRegistry.getSystemServiceClassName(name)
- + " from a non-visual Context. ";
- 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 "
- + "Context#createWindowContext(int, Bundle), which are adjusted to the "
- + "configuration and visual bounds of an area on screen.";
- final Exception exception = new IllegalAccessException(errorMessage);
- StrictMode.onIncorrectContextUsed(message, exception);
- Log.e(TAG, errorMessage + message, exception);
+ if (vmIncorrectContextUseEnabled()) {
+ // We may override this API from outer context.
+ final boolean isUiContext = isUiContext() || isOuterUiContext();
+ // Check incorrect Context usage.
+ if (isUiComponent(name) && !isUiContext) {
+ final String errorMessage = "Tried to access visual service "
+ + SystemServiceRegistry.getSystemServiceClassName(name)
+ + " 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 "
+ + "Context#createWindowContext(int, Bundle), which are adjusted to "
+ + "the configuration and visual bounds of an area on screen.";
+ final Exception exception = new IllegalAccessException(errorMessage);
+ StrictMode.onIncorrectContextUsed(message, exception);
+ Log.e(TAG, errorMessage + " " + message, exception);
+ }
}
-
return SystemServiceRegistry.getSystemService(this, name);
}
+ private boolean isOuterUiContext() {
+ return getOuterContext() != null && getOuterContext().isUiContext();
+ }
+
@Override
public String getSystemServiceName(Class<?> serviceClass) {
return SystemServiceRegistry.getSystemServiceName(serviceClass);
@@ -2370,6 +2377,7 @@
context.setResources(createResources(mToken, mPackageInfo, mSplitName, displayId,
overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(),
mResources.getLoaders()));
+ context.mIsUiContext = isUiContext() || isOuterUiContext();
return context;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 03b941d..f82ab7b 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);
@@ -4801,7 +4801,6 @@
contentView.setViewVisibility(R.id.time, View.GONE);
contentView.setImageViewIcon(R.id.profile_badge, null);
contentView.setViewVisibility(R.id.profile_badge, View.GONE);
- contentView.setViewVisibility(R.id.alerted_icon, View.GONE);
mN.mUsesStandardHeader = false;
}
@@ -5374,8 +5373,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 +8160,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 +9585,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 +10844,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/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index c20c11f..59997cc 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -101,11 +101,11 @@
import android.location.ILocationManager;
import android.location.LocationManager;
import android.media.AudioManager;
+import android.media.MediaFrameworkInitializer;
import android.media.MediaRouter;
import android.media.midi.IMidiManager;
import android.media.midi.MidiManager;
import android.media.projection.MediaProjectionManager;
-import android.media.session.MediaSessionManager;
import android.media.soundtrigger.SoundTriggerManager;
import android.media.tv.ITvInputManager;
import android.media.tv.TvInputManager;
@@ -855,13 +855,6 @@
return new ConsumerIrManager(ctx);
}});
- registerService(Context.MEDIA_SESSION_SERVICE, MediaSessionManager.class,
- new CachedServiceFetcher<MediaSessionManager>() {
- @Override
- public MediaSessionManager createService(ContextImpl ctx) {
- return new MediaSessionManager(ctx);
- }});
-
registerService(Context.TRUST_SERVICE, TrustManager.class,
new StaticServiceFetcher<TrustManager>() {
@Override
@@ -1335,6 +1328,7 @@
WifiFrameworkInitializer.registerServiceWrappers();
StatsFrameworkInitializer.registerServiceWrappers();
RollbackManagerFrameworkInitializer.initialize();
+ MediaFrameworkInitializer.registerServiceWrappers();
} finally {
// If any of the above code throws, we're in a pretty bad shape and the process
// will likely crash, but we'll reset it just in case there's an exception handler...
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index 786acba..621ef52 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -29,7 +29,7 @@
@SystemService(Context.TIME_ZONE_DETECTOR_SERVICE)
public interface TimeZoneDetector {
- /**
+ /**
* Returns the current user's time zone capabilities. See {@link TimeZoneCapabilities}.
*/
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 8c3268c..d2a774b 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1812,6 +1812,7 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
+ if (DBG) Log.d(TAG, "removeActiveDevice, profiles: " + profiles);
return mService.removeActiveDevice(profiles);
}
} catch (RemoteException e) {
@@ -1856,6 +1857,9 @@
try {
mServiceLock.readLock().lock();
if (mService != null) {
+ if (DBG) {
+ Log.d(TAG, "setActiveDevice, device: " + device + ", profiles: " + profiles);
+ }
return mService.setActiveDevice(device, profiles);
}
} catch (RemoteException e) {
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/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 565f34a..5fe094d 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1153,6 +1153,9 @@
*/
@Override
public boolean isUiContext() {
+ if (mBase == null) {
+ return false;
+ }
return mBase.isUiContext();
}
}
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 79da1f6..ee9bd3d 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -1168,7 +1168,12 @@
public int match(Uri data, boolean wildcardSupported) {
String host = data.getHost();
if (host == null) {
- return NO_MATCH_DATA;
+ if (wildcardSupported && mWild) {
+ // special case, if no host is provided, but the Authority is wildcard, match
+ return MATCH_CATEGORY_HOST;
+ } else {
+ return NO_MATCH_DATA;
+ }
}
if (false) Log.v("IntentFilter",
"Match host " + host + ": " + mHost);
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..f533760 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
@@ -4673,8 +4673,7 @@
* Marks an application exempt from having its permissions be automatically revoked when
* the app is unused for an extended period of time.
*
- * Only the installer on record that installed the given package, or a holder of
- * {@code WHITELIST_AUTO_REVOKE_PERMISSIONS} is allowed to call this.
+ * Only the installer on record that installed the given package is allowed to call this.
*
* Packages start in whitelisted state, and it is the installer's responsibility to
* un-whitelist the packages it installs, unless auto-revoking permissions from that package
@@ -7474,6 +7473,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/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 431c5d7..3688f1b 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -1510,7 +1510,7 @@
Uri data = null;
String dataType = null;
- String host = IntentFilter.WILDCARD;
+ String host = null;
final int numActions = intentInfo.countActions();
final int numSchemes = intentInfo.countDataSchemes();
final int numTypes = intentInfo.countDataTypes();
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..c7fc2ad 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();
}
@@ -628,7 +610,7 @@
serverCallback.sendResult(null /* data */);
}
}
- });
+ }, 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..2f8c97f 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 addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName);
- 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..9dacca7 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);
@@ -769,7 +768,7 @@
serverCallback.sendResult(null /* data */);
}
}
- });
+ }, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 7e251fd..ad58fea 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -33,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
@@ -57,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);
@@ -85,14 +84,17 @@
// 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);
+ void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName);
// Check if a client request is currently being handled
boolean isClientActive();
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/hardware/usb/UsbAccessory.java b/core/java/android/hardware/usb/UsbAccessory.java
index a94266b..f4cfc74 100644
--- a/core/java/android/hardware/usb/UsbAccessory.java
+++ b/core/java/android/hardware/usb/UsbAccessory.java
@@ -24,7 +24,6 @@
import android.os.RemoteException;
import com.android.internal.util.Preconditions;
-import java.util.Objects;
import java.util.Objects;
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index d16f070..ef305e2 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -160,6 +160,22 @@
"android.hardware.usb.action.USB_ACCESSORY_DETACHED";
/**
+ * Broadcast Action: A broadcast for USB accessory handshaking information delivery
+ *
+ * This intent is sent when a USB accessory connect attempt
+ *
+ * <p>For more information about communicating with USB accessory handshake, refer to
+ * <a href="https://source.android.com/devices/accessories/aoa">AOA</a> developer guide.</p>
+ *
+ * {@hide}
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ public static final String ACTION_USB_ACCESSORY_HANDSHAKE =
+ "android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE";
+
+ /**
* Boolean extra indicating whether USB is connected or disconnected.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast.
*
@@ -303,6 +319,52 @@
public static final String EXTRA_ACCESSORY = "accessory";
/**
+ * A long extra indicating ms from boot to get get_protocol UEvent
+ * This is obtained with SystemClock.elapsedRealtime()
+ * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts.
+ *
+ * {@hide}
+ */
+ @SystemApi
+ public static final String EXTRA_ACCESSORY_UEVENT_TIME =
+ "android.hardware.usb.extra.ACCESSORY_UEVENT_TIME";
+
+ /**
+ * An integer extra indicating numbers of send string during handshake
+ * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts
+ *
+ * <p>For more information about control request with identifying string information
+ * between communicating with USB accessory handshake, refer to
+ * <a href="https://source.android.com/devices/accessories/aoa">AOA</a> developer guide.</p>
+ *
+ * {@hide}
+ */
+ @SystemApi
+ public static final String EXTRA_ACCESSORY_STRING_COUNT =
+ "android.hardware.usb.extra.ACCESSORY_STRING_COUNT";
+
+ /**
+ * Boolean extra indicating whether got start accessory or not
+ * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts.
+ *
+ * {@hide}
+ */
+ @SystemApi
+ public static final String EXTRA_ACCESSORY_START =
+ "android.hardware.usb.extra.ACCESSORY_START";
+
+ /**
+ * A long extra indicating ms from boot to sent {@link #ACTION_USB_ACCESSORY_HANDSHAKE}
+ * This is obtained with SystemClock.elapsedRealtime()
+ * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts.
+ *
+ * {@hide}
+ */
+ @SystemApi
+ public static final String EXTRA_ACCESSORY_HANDSHAKE_END =
+ "android.hardware.usb.extra.ACCESSORY_HANDSHAKE_END";
+
+ /**
* Name of extra added to the {@link android.app.PendingIntent}
* passed into {@link #requestPermission(UsbDevice, PendingIntent)}
* or {@link #requestPermission(UsbAccessory, PendingIntent)}
diff --git a/core/java/android/inputmethodservice/InlineSuggestionSession.java b/core/java/android/inputmethodservice/InlineSuggestionSession.java
index 509cbe0..90d0ff0a 100644
--- a/core/java/android/inputmethodservice/InlineSuggestionSession.java
+++ b/core/java/android/inputmethodservice/InlineSuggestionSession.java
@@ -149,6 +149,11 @@
*/
@MainThread
void invalidate() {
+ try {
+ mCallback.onInlineSuggestionsSessionInvalidated();
+ } catch (RemoteException e) {
+ Log.w(TAG, "onInlineSuggestionsSessionInvalidated() remote exception:" + e);
+ }
if (mResponseCallback != null) {
consumeInlineSuggestionsResponse(EMPTY_RESPONSE);
mResponseCallback.invalidate();
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 5647bf9..c5a11abe 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -605,9 +605,6 @@
if (DEBUG) Log.v(TAG, "unbindInput(): binding=" + mInputBinding
+ " ic=" + mInputConnection);
// Unbind input is per process per display.
- // TODO(b/150902448): free-up IME surface when target is changing.
- // e.g. DisplayContent#setInputMethodTarget()
- removeImeSurface();
onUnbindInput();
mInputBinding = null;
mInputConnection = null;
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/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 0e10c42..0eb3c1e 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -38,7 +38,9 @@
* Representation of a MAC address.
*
* This class only supports 48 bits long addresses and does not support 64 bits long addresses.
- * Instances of this class are immutable.
+ * Instances of this class are immutable. This class provides implementations of hashCode()
+ * and equals() that make it suitable for use as keys in standard implementations of
+ * {@link java.util.Map}.
*/
public final class MacAddress implements Parcelable {
@@ -122,12 +124,22 @@
}
/**
+ * Convert this MacAddress to a byte array.
+ *
+ * The returned array is in network order. For example, if this MacAddress is 1:2:3:4:5:6,
+ * the returned array is [1, 2, 3, 4, 5, 6].
+ *
* @return a byte array representation of this MacAddress.
*/
public @NonNull byte[] toByteArray() {
return byteAddrFromLongAddr(mAddr);
}
+ /**
+ * Returns a human-readable representation of this MacAddress.
+ * The exact format is implementation-dependent and should not be assumed to have any
+ * particular format.
+ */
@Override
public @NonNull String toString() {
return stringAddrFromLongAddr(mAddr);
diff --git a/core/java/android/os/AndroidTimeoutException.java b/core/java/android/os/AndroidTimeoutException.java
deleted file mode 100644
index fdfcf8d..0000000
--- a/core/java/android/os/AndroidTimeoutException.java
+++ /dev/null
@@ -1,33 +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.os;
-
-import android.annotation.NonNull;
-import android.util.AndroidRuntimeException;
-
-/**
- * An exception that indicates that the request timed out, for example because
- * the requested content provider took too long to initialize.
- *
- * @hide
- */
-public final class AndroidTimeoutException extends AndroidRuntimeException {
-
- public AndroidTimeoutException(@NonNull String message) {
- super(message);
- }
-}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index ea7462b..a0207c8 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -122,6 +122,14 @@
private static native long getNativeFinalizer();
+ /**
+ * Returns the TID (task ID) for the current thread. Same as {@link Thread#getNativeTid()}
+ *
+ * @hide
+ */
+ @CriticalNative
+ public static native int getNativeTid();
+
// Use a Holder to allow static initialization of Binder in the boot image, and
// possibly to avoid some initialization ordering issues.
private static class NoImagePreloadHolder {
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/HwBinder.java b/core/java/android/os/HwBinder.java
index 7c42c36..64ab1d7 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -96,6 +96,15 @@
throws RemoteException, NoSuchElementException;
/**
+ * This allows getService to bypass the VINTF manifest for testing only.
+ *
+ * Disabled on user builds.
+ * @hide
+ */
+ public static native final void setTrebleTestingOverride(
+ boolean testingOverride);
+
+ /**
* Configures how many threads the process-wide hwbinder threadpool
* has to process incoming requests.
*
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/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
index d35ce5b..32c80e7 100644
--- a/core/java/android/os/incremental/V4Signature.java
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -159,7 +159,7 @@
*
* @param fileSize - size of the signed file (APK)
*/
- public static byte[] getSigningData(long fileSize, HashingInfo hashingInfo,
+ public static byte[] getSignedData(long fileSize, HashingInfo hashingInfo,
SigningInfo signingInfo) {
final int size =
4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize(
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 0abf8ae3..6b5eb16 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -86,7 +86,6 @@
import android.system.OsConstants;
import android.text.TextUtils;
import android.util.DataUnit;
-import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -161,11 +160,6 @@
/** {@hide} */
public static final String PROP_ISOLATED_STORAGE_SNAPSHOT = "sys.isolated_storage_snapshot";
/** {@hide} */
- public static final String PROP_FUSE = "persist.sys.fuse";
- /** {@hide} */
- public static final String PROP_SETTINGS_FUSE = FeatureFlagUtils.PERSIST_PREFIX
- + FeatureFlagUtils.SETTINGS_FUSE_FLAG;
- /** {@hide} */
public static final String PROP_FORCED_SCOPED_STORAGE_WHITELIST =
"forced_scoped_storage_whitelist";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d44c7fd..feebb94 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7883,19 +7883,26 @@
public static final String AWARE_TAP_PAUSE_TOUCH_COUNT = "aware_tap_pause_touch_count";
/**
+ * For user preference if swipe bottom to expand notification gesture enabled.
+ * @hide
+ */
+ public static final String SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED =
+ "swipe_bottom_to_notification_enabled";
+
+ /**
* For user preference if One-Handed Mode enabled.
* @hide
*/
public static final String ONE_HANDED_MODE_ENABLED = "one_handed_mode_enabled";
/**
- * For user perference if One-Handed Mode timeout.
+ * For user preference if One-Handed Mode timeout.
* @hide
*/
public static final String ONE_HANDED_MODE_TIMEOUT = "one_handed_mode_timeout";
/**
- * For user tapps app to exit One-Handed Mode.
+ * For user taps app to exit One-Handed Mode.
* @hide
*/
public static final String TAPS_APP_TO_EXIT = "taps_app_to_exit";
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 4c9afcf..fae26cf 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -3946,8 +3946,7 @@
/**
* The APN set id. When the user manually selects an APN or the framework sets an APN as
- * preferred, all APNs with the same set id as the selected APN should be prioritized over
- * APNs in other sets.
+ * preferred, the device can only use APNs with the same set id as the selected APN.
* <p>Type: INTEGER</p>
* @hide
*/
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 2677087..5d34c47 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1938,6 +1938,17 @@
/**
* @hide
*/
+ public @NonNull Ranking withAudiblyAlertedInfo(@Nullable Ranking previous) {
+ if (previous != null && previous.mLastAudiblyAlertedMs > 0
+ && this.mLastAudiblyAlertedMs <= 0) {
+ this.mLastAudiblyAlertedMs = previous.mLastAudiblyAlertedMs;
+ }
+ return this;
+ }
+
+ /**
+ * @hide
+ */
public void populate(Ranking other) {
populate(other.mKey,
other.mRank,
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/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 537498c..e338fd97 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -41,7 +41,6 @@
public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
public static final String DYNAMIC_SYSTEM = "settings_dynamic_system";
public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
- public static final String SETTINGS_FUSE_FLAG = "settings_fuse";
/** @hide */
public static final String SETTINGS_DO_NOT_RESTORE_PRESERVED =
"settings_do_not_restore_preserved";
@@ -52,7 +51,6 @@
DEFAULT_FLAGS = new HashMap<>();
DEFAULT_FLAGS.put("settings_audio_switcher", "true");
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
- DEFAULT_FLAGS.put(SETTINGS_FUSE_FLAG, "true");
DEFAULT_FLAGS.put(DYNAMIC_SYSTEM, "false");
DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false");
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
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/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 346fe29..6e34666 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -24,7 +24,6 @@
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
-import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
import android.util.ArrayMap;
@@ -213,11 +212,9 @@
verityDigest, apk.length(), signatureInfo);
}
- byte[] digest = pickBestDigestForV4(contentDigests);
-
return new VerifiedSigner(
signerCerts.toArray(new X509Certificate[signerCerts.size()][]),
- verityRootHash, digest);
+ verityRootHash, contentDigests);
}
private static X509Certificate[] verifySigner(
@@ -339,8 +336,7 @@
} catch (CertificateException e) {
throw new SecurityException("Failed to decode certificate #" + certificateCount, e);
}
- certificate = new VerbatimX509Certificate(
- certificate, encodedCert);
+ certificate = new VerbatimX509Certificate(certificate, encodedCert);
certs.add(certificate);
}
@@ -434,12 +430,15 @@
public final X509Certificate[][] certs;
public final byte[] verityRootHash;
- public final byte[] digest;
+ // Algorithm -> digest map of signed digests in the signature.
+ // All these are verified if requested.
+ public final Map<Integer, byte[]> contentDigests;
- public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash, byte[] digest) {
+ public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash,
+ Map<Integer, byte[]> contentDigests) {
this.certs = certs;
this.verityRootHash = verityRootHash;
- this.digest = digest;
+ this.contentDigests = contentDigests;
}
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 4ab541b..9357285 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -24,7 +24,6 @@
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
-import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
import android.os.Build;
@@ -161,7 +160,7 @@
boolean doVerifyIntegrity) throws SecurityException, IOException {
int signerCount = 0;
Map<Integer, byte[]> contentDigests = new ArrayMap<>();
- VerifiedSigner result = null;
+ Pair<X509Certificate[], VerifiedProofOfRotation> result = null;
CertificateFactory certFactory;
try {
certFactory = CertificateFactory.getInstance("X.509");
@@ -206,18 +205,17 @@
ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo);
}
+ byte[] verityRootHash = null;
if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
byte[] verityDigest = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
- result.verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
+ verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
verityDigest, apk.length(), signatureInfo);
}
- result.digest = pickBestDigestForV4(contentDigests);
-
- return result;
+ return new VerifiedSigner(result.first, result.second, verityRootHash, contentDigests);
}
- private static VerifiedSigner verifySigner(
+ private static Pair<X509Certificate[], VerifiedProofOfRotation> verifySigner(
ByteBuffer signerBlock,
Map<Integer, byte[]> contentDigests,
CertificateFactory certFactory)
@@ -349,8 +347,7 @@
} catch (CertificateException e) {
throw new SecurityException("Failed to decode certificate #" + certificateCount, e);
}
- certificate = new VerbatimX509Certificate(
- certificate, encodedCert);
+ certificate = new VerbatimX509Certificate(certificate, encodedCert);
certs.add(certificate);
}
@@ -382,8 +379,9 @@
private static final int PROOF_OF_ROTATION_ATTR_ID = 0x3ba06f8c;
- private static VerifiedSigner verifyAdditionalAttributes(ByteBuffer attrs,
- List<X509Certificate> certs, CertificateFactory certFactory) throws IOException {
+ private static Pair<X509Certificate[], VerifiedProofOfRotation> verifyAdditionalAttributes(
+ ByteBuffer attrs, List<X509Certificate> certs, CertificateFactory certFactory)
+ throws IOException {
X509Certificate[] certChain = certs.toArray(new X509Certificate[certs.size()]);
VerifiedProofOfRotation por = null;
@@ -421,7 +419,7 @@
break;
}
}
- return new VerifiedSigner(certChain, por);
+ return Pair.create(certChain, por);
}
private static VerifiedProofOfRotation verifyProofOfRotationStruct(
@@ -570,12 +568,17 @@
public final X509Certificate[] certs;
public final VerifiedProofOfRotation por;
- public byte[] verityRootHash;
- public byte[] digest;
+ public final byte[] verityRootHash;
+ // Algorithm -> digest map of signed digests in the signature.
+ // All these are verified if requested.
+ public final Map<Integer, byte[]> contentDigests;
- public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por) {
+ public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por,
+ byte[] verityRootHash, Map<Integer, byte[]> contentDigests) {
this.certs = certs;
this.por = por;
+ this.verityRootHash = verityRootHash;
+ this.contentDigests = contentDigests;
}
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
index d40efce..844816c 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
@@ -16,12 +16,14 @@
package android.util.apk;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
import android.os.incremental.IncrementalManager;
import android.os.incremental.V4Signature;
+import android.util.ArrayMap;
import android.util.Pair;
import java.io.ByteArrayInputStream;
@@ -42,6 +44,7 @@
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
+import java.util.Map;
/**
* APK Signature Scheme v4 verifier.
@@ -79,13 +82,20 @@
throw new SignatureNotFoundException("Failed to read V4 signature.", e);
}
- final byte[] signedData = V4Signature.getSigningData(apk.length(), hashingInfo,
+ // Verify signed data and extract certificates and apk digest.
+ final byte[] signedData = V4Signature.getSignedData(apk.length(), hashingInfo,
signingInfo);
+ final Pair<Certificate, byte[]> result = verifySigner(signingInfo, signedData);
- return verifySigner(signingInfo, signedData);
+ // Populate digests enforced by IncFS driver.
+ Map<Integer, byte[]> contentDigests = new ArrayMap<>();
+ contentDigests.put(convertToContentDigestType(hashingInfo.hashAlgorithm),
+ hashingInfo.rawRootHash);
+
+ return new VerifiedSigner(new Certificate[]{result.first}, result.second, contentDigests);
}
- private static VerifiedSigner verifySigner(V4Signature.SigningInfo signingInfo,
+ private static Pair<Certificate, byte[]> verifySigner(V4Signature.SigningInfo signingInfo,
final byte[] signedData) throws SecurityException {
if (!isSupportedSignatureAlgorithm(signingInfo.signatureAlgorithmId)) {
throw new SecurityException("No supported signatures found");
@@ -145,21 +155,34 @@
"Public key mismatch between certificate and signature record");
}
- return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.apkDigest);
+ return Pair.create(certificate, signingInfo.apkDigest);
+ }
+
+ private static int convertToContentDigestType(int hashAlgorithm) throws SecurityException {
+ if (hashAlgorithm == V4Signature.HASHING_ALGORITHM_SHA256) {
+ return CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
+ }
+ throw new SecurityException("Unsupported hashAlgorithm: " + hashAlgorithm);
}
/**
- * Verified APK Signature Scheme v4 signer, including V3 digest.
+ * Verified APK Signature Scheme v4 signer, including V2/V3 digest.
*
* @hide for internal use only.
*/
public static class VerifiedSigner {
public final Certificate[] certs;
- public byte[] apkDigest;
+ public final byte[] apkDigest;
- public VerifiedSigner(Certificate[] certs, byte[] apkDigest) {
+ // Algorithm -> digest map of signed digests in the signature.
+ // These are continuously enforced by the IncFS driver.
+ public final Map<Integer, byte[]> contentDigests;
+
+ public VerifiedSigner(Certificate[] certs, byte[] apkDigest,
+ Map<Integer, byte[]> contentDigests) {
this.certs = certs;
this.apkDigest = apkDigest;
+ this.contentDigests = contentDigests;
}
}
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index ab8f80d3..e0258f7 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -45,6 +45,7 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.ZipEntry;
@@ -184,21 +185,21 @@
Signature[] signerSigs = convertToSignatures(signerCerts);
if (verifyFull) {
- byte[] nonstreamingDigest = null;
- Certificate[][] nonstreamingCerts = null;
+ Map<Integer, byte[]> nonstreamingDigests;
+ Certificate[][] nonstreamingCerts;
try {
// v4 is an add-on and requires v2 or v3 signature to validate against its
// certificate and digest
ApkSignatureSchemeV3Verifier.VerifiedSigner v3Signer =
ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
- nonstreamingDigest = v3Signer.digest;
+ nonstreamingDigests = v3Signer.contentDigests;
nonstreamingCerts = new Certificate[][]{v3Signer.certs};
} catch (SignatureNotFoundException e) {
try {
ApkSignatureSchemeV2Verifier.VerifiedSigner v2Signer =
ApkSignatureSchemeV2Verifier.verify(apkPath, false);
- nonstreamingDigest = v2Signer.digest;
+ nonstreamingDigests = v2Signer.contentDigests;
nonstreamingCerts = v2Signer.certs;
} catch (SignatureNotFoundException ee) {
throw new SecurityException(
@@ -220,8 +221,15 @@
}
}
- if (!ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest,
- vSigner.apkDigest.length)) {
+ boolean found = false;
+ for (byte[] nonstreamingDigest : nonstreamingDigests.values()) {
+ if (ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest,
+ vSigner.apkDigest.length)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
throw new SecurityException("APK digest in V4 signature does not match V2/V3");
}
}
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 6efe95c..990092c 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -577,21 +577,6 @@
}
/**
- * Returns the best digest from the map of available digests.
- * similarly to compareContentDigestAlgorithm.
- *
- * Keep in sync with pickBestDigestForV4 in apksigner's ApkSigningBlockUtils.
- */
- static byte[] pickBestDigestForV4(Map<Integer, byte[]> contentDigests) {
- for (int algo : V4_CONTENT_DIGEST_ALGORITHMS) {
- if (contentDigests.containsKey(algo)) {
- return contentDigests.get(algo);
- }
- }
- return null;
- }
-
- /**
* Returns new byte buffer whose content is a shared subsequence of this buffer's content
* between the specified start (inclusive) and end (exclusive) positions. As opposed to
* {@link ByteBuffer#slice()}, the returned buffer's byte order is the same as the source
diff --git a/core/java/android/view/IDisplayWindowInsetsController.aidl b/core/java/android/view/IDisplayWindowInsetsController.aidl
index 429c3ae..a0d4a65 100644
--- a/core/java/android/view/IDisplayWindowInsetsController.aidl
+++ b/core/java/android/view/IDisplayWindowInsetsController.aidl
@@ -27,6 +27,13 @@
oneway interface IDisplayWindowInsetsController {
/**
+ * Called when top focused window changes to determine whether or not to take over insets
+ * control. Won't be called if config_remoteInsetsControllerControlsSystemBars is false.
+ * @param packageName: Passes the top package name
+ */
+ void topFocusedWindowChanged(String packageName);
+
+ /**
* @see IWindow#insetsChanged
*/
void insetsChanged(in InsetsState insetsState);
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 58597cf..00fc672 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -388,16 +388,6 @@
oneway void hideTransientBars(int displayId);
/**
- * When set to {@code true} the system bars will always be shown. This is true even if an app
- * requests to be fullscreen by setting the system ui visibility flags. The
- * functionality was added for the automotive case as a way to guarantee required content stays
- * on screen at all times.
- *
- * @hide
- */
- oneway void setForceShowSystemBars(boolean show);
-
- /**
* Called by System UI to notify of changes to the visibility of Recents.
*/
oneway void setRecentsVisibility(boolean visible);
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index ef9d990..c1998c6 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -119,11 +119,11 @@
// If we had a request before to show from IME (tracked with mImeRequestedShow), reaching
// this code here means that we now got control, so we can start the animation immediately.
// If client window is trying to control IME and IME is already visible, it is immediate.
- if (fromIme || mState.getSource(getType()).isVisible()) {
+ if (fromIme || mState.getSource(getType()).isVisible() && getControl() != null) {
return ShowResult.SHOW_IMMEDIATELY;
}
- return getImm().requestImeShow(null /* resultReceiver */)
+ return getImm().requestImeShow(mController.getHost().getWindowToken())
? ShowResult.IME_SHOW_DELAYED : ShowResult.IME_SHOW_FAILED;
}
@@ -132,12 +132,15 @@
*/
@Override
void notifyHidden() {
- getImm().notifyImeHidden();
+ getImm().notifyImeHidden(mController.getHost().getWindowToken());
}
@Override
public void removeSurface() {
- getImm().removeImeSurface();
+ final IBinder window = mController.getHost().getWindowToken();
+ if (window != null) {
+ getImm().removeImeSurface(window);
+ }
}
@Override
@@ -146,6 +149,7 @@
super.setControl(control, showTypes, hideTypes);
if (control == null && !mIsRequestedVisibleAwaitingControl) {
hide();
+ removeSurface();
}
}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 40e6f4b..700dc66 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -153,6 +153,9 @@
if (oldLeash == null || newLeash == null || !oldLeash.isSameSurface(newLeash)) {
applyHiddenToControl();
}
+ if (!requestedVisible && !mIsAnimationPending) {
+ removeSurface();
+ }
}
}
if (lastControl != null) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 13f11dc..2168fe3 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -13800,6 +13800,17 @@
}
}
+ private void notifySubtreeAccessibilityStateChangedByParentIfNeeded() {
+ if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
+ return;
+ }
+
+ final View sendA11yEventView = (View) getParentForAccessibility();
+ if (sendA11yEventView != null && sendA11yEventView.isShown()) {
+ sendA11yEventView.notifySubtreeAccessibilityStateChangedIfNeeded();
+ }
+ }
+
/**
* Changes the visibility of this View without triggering any other changes. This should only
* be used by animation frameworks, such as {@link android.transition.Transition}, where
@@ -16229,7 +16240,13 @@
((!(mParent instanceof ViewGroup)) || ((ViewGroup) mParent).isShown())) {
dispatchVisibilityAggregated(newVisibility == VISIBLE);
}
- notifySubtreeAccessibilityStateChangedIfNeeded();
+ // If this view is invisible from visible, then sending the A11y event by its
+ // parent which is shown and has the accessibility important.
+ if ((old & VISIBILITY_MASK) == VISIBLE) {
+ notifySubtreeAccessibilityStateChangedByParentIfNeeded();
+ } else {
+ notifySubtreeAccessibilityStateChangedIfNeeded();
+ }
}
}
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/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 81a78dc..b022d2a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2717,7 +2717,6 @@
mAttachInfo.mThreadedRenderer.isEnabled()) {
mAttachInfo.mThreadedRenderer.destroy();
}
- notifySurfaceDestroyed();
} else if ((surfaceReplaced
|| surfaceSizeChanged || windowRelayoutWasForced || colorModeChanged)
&& mSurfaceHolder == null
@@ -2948,6 +2947,10 @@
}
}
+ if (surfaceDestroyed) {
+ notifySurfaceDestroyed();
+ }
+
if (triggerGlobalLayoutListener) {
mAttachInfo.mRecomputeGlobalAttributes = false;
mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
@@ -9382,6 +9385,11 @@
return mInputEventReceiver.getToken();
}
+ @NonNull
+ public IBinder getWindowToken() {
+ return mAttachInfo.mWindowToken;
+ }
+
/**
* Class for managing the accessibility interaction connection
* based on the global accessibility state.
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/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 864c40f..fa92e29 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2111,28 +2111,36 @@
/**
* Call showSoftInput with currently focused view.
- * @return {@code true} if IME can be shown.
+ *
+ * @param windowToken the window from which this request originates. If this doesn't match the
+ * currently served view, the request is ignored and returns {@code false}.
+ *
+ * @return {@code true} if IME can (eventually) be shown, {@code false} otherwise.
* @hide
*/
- public boolean requestImeShow(ResultReceiver resultReceiver) {
+ public boolean requestImeShow(IBinder windowToken) {
synchronized (mH) {
final View servedView = getServedViewLocked();
- if (servedView == null) {
+ if (servedView == null || servedView.getWindowToken() != windowToken) {
return false;
}
- showSoftInput(servedView, 0 /* flags */, resultReceiver);
+ showSoftInput(servedView, 0 /* flags */, null /* resultReceiver */);
return true;
}
}
/**
* Notify IME directly that it is no longer visible.
+ *
+ * @param windowToken the window from which this request originates. If this doesn't match the
+ * currently served view, the request is ignored.
* @hide
*/
- public void notifyImeHidden() {
+ public void notifyImeHidden(IBinder windowToken) {
synchronized (mH) {
try {
- if (mCurMethod != null) {
+ if (mCurMethod != null && mCurRootView != null
+ && mCurRootView.getWindowToken() == windowToken) {
mCurMethod.notifyImeHidden();
}
} catch (RemoteException re) {
@@ -2142,15 +2150,15 @@
/**
* Notify IME directly to remove surface as it is no longer visible.
+ * @param windowToken The client window token that requests the IME to remove its surface.
* @hide
*/
- public void removeImeSurface() {
+ public void removeImeSurface(IBinder windowToken) {
synchronized (mH) {
try {
- if (mCurMethod != null) {
- mCurMethod.removeImeSurface();
- }
- } catch (RemoteException re) {
+ mService.removeImeSurfaceFromWindow(windowToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
}
diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java
index ff3ac073..9eb63087 100644
--- a/core/java/android/widget/EditorTouchState.java
+++ b/core/java/android/widget/EditorTouchState.java
@@ -174,12 +174,9 @@
int touchSlop = config.getScaledTouchSlop();
mMovedEnoughForDrag = distanceSquared > touchSlop * touchSlop;
if (mMovedEnoughForDrag) {
- // If the direction of the swipe motion is within 30 degrees of vertical, it is
- // considered a vertical drag. We don't actually have to compute the angle to
- // implement the check though. When the angle is exactly 30 degrees from
- // vertical, 2*deltaX = distance. When the angle is less than 30 degrees from
- // vertical, 2*deltaX < distance.
- mIsDragCloseToVertical = (4 * deltaXSquared) <= distanceSquared;
+ // If the direction of the swipe motion is within 45 degrees of vertical, it is
+ // considered a vertical drag.
+ mIsDragCloseToVertical = Math.abs(deltaX) <= Math.abs(deltaY);
}
}
} else if (action == MotionEvent.ACTION_CANCEL) {
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 843700c..32c68bd 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -108,7 +108,7 @@
*
* @return the swap result, index 0 is the start index and index 1 is the end index.
*/
- private static int[] sortSelctionIndices(int selectionStart, int selectionEnd) {
+ private static int[] sortSelectionIndices(int selectionStart, int selectionEnd) {
if (selectionStart < selectionEnd) {
return new int[]{selectionStart, selectionEnd};
}
@@ -122,11 +122,11 @@
* @param textView the selected TextView.
* @return the swap result, index 0 is the start index and index 1 is the end index.
*/
- private static int[] sortSelctionIndicesFromTextView(TextView textView) {
+ private static int[] sortSelectionIndicesFromTextView(TextView textView) {
int selectionStart = textView.getSelectionStart();
int selectionEnd = textView.getSelectionEnd();
- return sortSelctionIndices(selectionStart, selectionEnd);
+ return sortSelectionIndices(selectionStart, selectionEnd);
}
/**
@@ -135,7 +135,7 @@
public void startSelectionActionModeAsync(boolean adjustSelection) {
// Check if the smart selection should run for editable text.
adjustSelection &= getTextClassificationSettings().isSmartSelectionEnabled();
- int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+ int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
mSelectionTracker.onOriginalSelection(
getText(mTextView),
@@ -165,7 +165,7 @@
* Starts Link ActionMode.
*/
public void startLinkActionModeAsync(int start, int end) {
- int[] indexResult = sortSelctionIndices(start, end);
+ int[] indexResult = sortSelectionIndices(start, end);
mSelectionTracker.onOriginalSelection(getText(mTextView), indexResult[0], indexResult[1],
true /*isLink*/);
cancelAsyncTask();
@@ -201,21 +201,21 @@
/** Reports a selection action event. */
public void onSelectionAction(int menuItemId, @Nullable String actionLabel) {
- int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+ int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
mSelectionTracker.onSelectionAction(
sortedSelectionIndices[0], sortedSelectionIndices[1],
getActionType(menuItemId), actionLabel, mTextClassification);
}
public void onSelectionDrag() {
- int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+ int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
mSelectionTracker.onSelectionAction(
sortedSelectionIndices[0], sortedSelectionIndices[1],
SelectionEvent.ACTION_DRAG, /* actionLabel= */ null, mTextClassification);
}
public void onTextChanged(int start, int end) {
- int[] sortedSelectionIndices = sortSelctionIndices(start, end);
+ int[] sortedSelectionIndices = sortSelectionIndices(start, end);
mSelectionTracker.onTextChanged(sortedSelectionIndices[0], sortedSelectionIndices[1],
mTextClassification);
}
@@ -334,7 +334,7 @@
startSelectionActionMode(startSelectionResult);
};
// TODO do not trigger the animation if the change included only non-printable characters
- int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+ int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
final boolean didSelectionChange =
result != null && (sortedSelectionIndices[0] != result.mStart
|| sortedSelectionIndices[1] != result.mEnd);
@@ -486,7 +486,7 @@
if (actionMode != null) {
actionMode.invalidate();
}
- final int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+ final int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
mSelectionTracker.onSelectionUpdated(
sortedSelectionIndices[0], sortedSelectionIndices[1], mTextClassification);
mTextClassificationAsyncTask = null;
@@ -495,7 +495,7 @@
private void resetTextClassificationHelper(int selectionStart, int selectionEnd) {
if (selectionStart < 0 || selectionEnd < 0) {
// Use selection indices
- int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+ int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
selectionStart = sortedSelectionIndices[0];
selectionEnd = sortedSelectionIndices[1];
}
@@ -637,7 +637,7 @@
mAllowReset = false;
boolean selected = editor.selectCurrentWord();
if (selected) {
- final int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(textView);
+ final int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(textView);
mSelectionStart = sortedSelectionIndices[0];
mSelectionEnd = sortedSelectionIndices[1];
mLogger.logSelectionAction(
@@ -1215,7 +1215,7 @@
SelectionResult(int start, int end,
@Nullable TextClassification classification, @Nullable TextSelection selection) {
- int[] sortedIndices = sortSelctionIndices(start, end);
+ int[] sortedIndices = sortSelectionIndices(start, end);
mStart = sortedIndices[0];
mEnd = sortedIndices[1];
mClassification = classification;
diff --git a/core/java/android/widget/TextViewRichContentReceiver.java b/core/java/android/widget/TextViewRichContentReceiver.java
index 5678a1e..4f2d954 100644
--- a/core/java/android/widget/TextViewRichContentReceiver.java
+++ b/core/java/android/widget/TextViewRichContentReceiver.java
@@ -116,9 +116,6 @@
private static boolean onReceiveForAutofill(@NonNull TextView textView, @NonNull ClipData clip,
@Flags int flags) {
final CharSequence text = coerceToText(clip, textView.getContext(), flags);
- if (text.length() == 0) {
- return false;
- }
// First autofill it...
textView.setText(text);
// ...then move cursor to the end.
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 476198b..bbfd07b 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -358,6 +358,8 @@
createNativeLibrarySubdir(subDir);
}
+ // Even if extractNativeLibs is false, we still need to check if the native libs in the APK
+ // are valid. This is done in the native code.
int copyRet = copyNativeBinaries(handle, subDir, supportedAbi);
if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
return copyRet;
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/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index cc55cff..e09ef49 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -27,6 +27,7 @@
import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IntArray;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -126,6 +127,11 @@
}
};
+ private final Object mNativeTidsLock = new Object();
+ // @GuardedBy("mNativeTidsLock") // Cannot mark it as "GuardedBy" because it's read
+ // directly, as a volatile field.
+ private volatile IntArray mNativeTids = new IntArray(0);
+
/** Injector for {@link BinderCallsStats}. */
public static class Injector {
public Random getRandomGenerator() {
@@ -175,6 +181,8 @@
return null;
}
+ noteNativeThreadId();
+
final CallSession s = obtainCallSession();
s.binderClass = binder.getClass();
s.transactionCode = code;
@@ -312,6 +320,27 @@
}
}
+ private void noteNativeThreadId() {
+ final int tid = getNativeTid();
+ int index = mNativeTids.binarySearch(tid);
+ if (index >= 0) {
+ return;
+ }
+
+ // Use the copy-on-write approach. The changes occur exceedingly infrequently, so
+ // this code path is exercised just a few times per boot
+ synchronized (mNativeTidsLock) {
+ IntArray nativeTids = mNativeTids;
+ index = nativeTids.binarySearch(tid);
+ if (index < 0) {
+ IntArray copyOnWriteArray = new IntArray(nativeTids.size() + 1);
+ copyOnWriteArray.addAll(nativeTids);
+ copyOnWriteArray.add(-index - 1, tid);
+ mNativeTids = copyOnWriteArray;
+ }
+ }
+ }
+
/**
* This method is expensive to call.
*/
@@ -505,6 +534,17 @@
return Binder.getCallingUid();
}
+ protected int getNativeTid() {
+ return Binder.getNativeTid();
+ }
+
+ /**
+ * Returns known Linux TIDs for threads taking incoming binder calls.
+ */
+ public int[] getNativeTids() {
+ return mNativeTids.toArray();
+ }
+
protected long getElapsedRealtimeMicro() {
return SystemClock.elapsedRealtimeNanos() / 1000;
}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 0b46658..0a3fe09 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -21,8 +21,6 @@
import static android.view.View.SYSTEM_UI_LAYOUT_FLAGS;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.WindowInsets.Type.ime;
-import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
@@ -34,8 +32,6 @@
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -146,17 +142,7 @@
if ((view.getWindowSystemUiVisibility() & SYSTEM_UI_LAYOUT_FLAGS) != 0) {
return new Pair<>(Insets.NONE, insets);
}
-
- boolean includeIme = (view.getViewRootImpl().mWindowAttributes.softInputMode
- & SOFT_INPUT_MASK_ADJUST)
- == SOFT_INPUT_ADJUST_RESIZE;
- Insets insetsToApply;
- if (ViewRootImpl.sNewInsetsMode == 0) {
- insetsToApply = insets.getSystemWindowInsets();
- } else {
- insetsToApply = insets.getInsets(systemBars() | (includeIme ? ime() : 0));
- }
- insets = insets.inset(insetsToApply);
+ Insets insetsToApply = insets.getSystemWindowInsets();
return new Pair<>(insetsToApply,
insets.inset(insetsToApply).consumeSystemWindowInsets());
};
diff --git a/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl b/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl
index cf1220c..03948a9 100644
--- a/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl
+++ b/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl
@@ -58,4 +58,7 @@
// #onFinishInput()} is called on the field specified by the earlier
// {@link #onInputMethodStartInput(AutofillId)}.
void onInputMethodFinishInput();
+
+ // Indicates that the current IME changes inline suggestion session.
+ void onInlineSuggestionsSessionInvalidated();
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 8ec51b8..a1cbd3f 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -73,5 +73,8 @@
in float[] matrixValues);
oneway void reportPerceptible(in IBinder windowToken, boolean perceptible);
+ /** Remove the IME surface. Requires INTERNAL_SYSTEM_WINDOW permission. */
void removeImeSurface();
+ /** Remove the IME surface. Requires passing the currently focused window. */
+ void removeImeSurfaceFromWindow(in IBinder windowToken);
}
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
index 71edfd5..350f358 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -61,8 +61,8 @@
mInfo.name = getStringField(env, obj, gInputApplicationHandleClassInfo.name, "<null>");
- mInfo.dispatchingTimeout = env->GetLongField(obj,
- gInputApplicationHandleClassInfo.dispatchingTimeoutNanos);
+ mInfo.dispatchingTimeout = decltype(mInfo.dispatchingTimeout)(
+ env->GetLongField(obj, gInputApplicationHandleClassInfo.dispatchingTimeoutNanos));
jobject tokenObj = env->GetObjectField(obj,
gInputApplicationHandleClassInfo.token);
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 81569e0..792c005 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -117,8 +117,8 @@
gInputWindowHandleClassInfo.layoutParamsFlags);
mInfo.layoutParamsType = env->GetIntField(obj,
gInputWindowHandleClassInfo.layoutParamsType);
- mInfo.dispatchingTimeout = env->GetLongField(obj,
- gInputWindowHandleClassInfo.dispatchingTimeoutNanos);
+ mInfo.dispatchingTimeout = decltype(mInfo.dispatchingTimeout)(
+ env->GetLongField(obj, gInputWindowHandleClassInfo.dispatchingTimeoutNanos));
mInfo.frameLeft = env->GetIntField(obj,
gInputWindowHandleClassInfo.frameLeft);
mInfo.frameTop = env->GetIntField(obj,
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index b6427c9a..48f33a6 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -339,6 +339,10 @@
return JHwRemoteBinder::NewObject(env, service);
}
+void JHwBinder_native_setTrebleTestingOverride(JNIEnv*, jclass, jboolean testingOverride) {
+ hardware::details::setTrebleTestingOverride(testingOverride);
+}
+
void JHwBinder_native_configureRpcThreadpool(JNIEnv *, jclass,
jlong maxThreads, jboolean callerWillJoin) {
CHECK(maxThreads > 0);
@@ -368,6 +372,9 @@
{ "getService", "(Ljava/lang/String;Ljava/lang/String;Z)L" PACKAGE_PATH "/IHwBinder;",
(void *)JHwBinder_native_getService },
+ { "setTrebleTestingOverride", "(Z)V",
+ (void *)JHwBinder_native_setTrebleTestingOverride },
+
{ "configureRpcThreadpool", "(JZ)V",
(void *)JHwBinder_native_configureRpcThreadpool },
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index a88f891..ff336ee 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -122,10 +122,18 @@
std::stringstream ss;
ss << "HwBinder Error: (" << err << ")";
- jniThrowException(
- env,
- canThrowRemoteException ? "android/os/RemoteException" : "java/lang/RuntimeException",
- ss.str().c_str());
+ const char* exception = nullptr;
+ if (canThrowRemoteException) {
+ if (err == DEAD_OBJECT) {
+ exception = "android/os/DeadObjectException";
+ } else {
+ exception = "android/os/RemoteException";
+ }
+ } else {
+ exception = "java/lang/RuntimeException";
+ }
+
+ jniThrowException(env, exception, ss.str().c_str());
break;
}
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 4c9d7ab..885b0a3 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -30,6 +30,7 @@
#include <unistd.h>
#include <android-base/stringprintf.h>
+#include <android-base/threads.h>
#include <binder/BpBinder.h>
#include <binder/IInterface.h>
#include <binder/IPCThreadState.h>
@@ -1047,6 +1048,10 @@
jbh->setExtension(extension);
}
+static jint android_os_Binder_getNativeTid() {
+ return (jint)android::base::GetThreadId();
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gBinderMethods[] = {
@@ -1078,6 +1083,8 @@
{ "blockUntilThreadAvailable", "()V", (void*)android_os_Binder_blockUntilThreadAvailable },
{ "getExtension", "()Landroid/os/IBinder;", (void*)android_os_Binder_getExtension },
{ "setExtension", "(Landroid/os/IBinder;)V", (void*)android_os_Binder_setExtension },
+ // @CriticalNative
+ { "getNativeTid", "()I", (void*)android_os_Binder_getNativeTid },
};
const char* const kBinderPathName = "android/os/Binder";
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 6e49c0d..b7c5289 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -124,7 +124,6 @@
static pid_t gSystemServerPid = 0;
static constexpr const char* kVoldAppDataIsolation = "persist.sys.vold_app_data_isolation_enabled";
-static constexpr const char* kPropFuse = "persist.sys.fuse";
static const char kZygoteClassName[] = "com/android/internal/os/Zygote";
static jclass gZygoteClass;
static jmethodID gCallPostForkSystemServerHooks;
@@ -836,29 +835,20 @@
PrepareDir(user_source, 0710, user_id ? AID_ROOT : AID_SHELL,
multiuser_get_uid(user_id, AID_EVERYBODY), fail_fn);
- bool isFuse = GetBoolProperty(kPropFuse, false);
bool isAppDataIsolationEnabled = GetBoolProperty(kVoldAppDataIsolation, false);
- if (isFuse) {
- if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) {
+ if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) {
const std::string pass_through_source = StringPrintf("/mnt/pass_through/%d", user_id);
PrepareDir(pass_through_source, 0710, AID_ROOT, AID_MEDIA_RW, fail_fn);
BindMount(pass_through_source, "/storage", fail_fn);
- } else if (mount_mode == MOUNT_EXTERNAL_INSTALLER) {
+ } else if (mount_mode == MOUNT_EXTERNAL_INSTALLER) {
const std::string installer_source = StringPrintf("/mnt/installer/%d", user_id);
BindMount(installer_source, "/storage", fail_fn);
- } else if (isAppDataIsolationEnabled && mount_mode == MOUNT_EXTERNAL_ANDROID_WRITABLE) {
+ } else if (isAppDataIsolationEnabled && mount_mode == MOUNT_EXTERNAL_ANDROID_WRITABLE) {
const std::string writable_source = StringPrintf("/mnt/androidwritable/%d", user_id);
BindMount(writable_source, "/storage", fail_fn);
- } else {
- BindMount(user_source, "/storage", fail_fn);
- }
} else {
- const std::string& storage_source = ExternalStorageViews[mount_mode];
- BindMount(storage_source, "/storage", fail_fn);
-
- // Mount user-specific symlink helper into place
- BindMount(user_source, "/storage/self", fail_fn);
+ BindMount(user_source, "/storage", fail_fn);
}
}
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 50b5ead..097af76 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -1722,7 +1722,7 @@
// OPEN: Settings > System > Language & Region
SETTINGS_LANGUAGE_CATEGORY = 750;
- // OPEN: Settings > System > Input & Gesture > Swipe to notification gesture
+ // OPEN: Settings > System > Input & Gesture > Swipe fingerprint for notifications
SETTINGS_GESTURE_SWIPE_TO_NOTIFICATION = 751;
// OPEN: Settings > System > Input & Gesture > Double tap power button gesture
@@ -2688,4 +2688,9 @@
// CATEGORY: SETTINGS
// OS: R
MEDIA_CONTROLS_SETTINGS = 1845;
+
+ // OPEN: Settings > System > Gestures > Swipe for notification
+ // CATEGORY: SETTINGS
+ // OS: R QPR
+ SETTINGS_SWIPE_BOTTOM_TO_NOTIFICATION = 1846;
}
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 7627961..edafb0c 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -381,6 +381,15 @@
}
optional Notification notification = 41;
+ message OneHanded {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ optional SettingProto one_handed_mode_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto one_handed_mode_timeout = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto taps_app_to_exit = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional OneHanded onehanded = 80;
+
message PackageVerifier {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -502,6 +511,7 @@
// parent profile.
optional SettingProto sync_parent_sounds = 55 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto system_navigation_keys_enabled = 56 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto swipe_bottom_to_notification_enabled = 82 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto theme_customization_overlay_packages = 75 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto trust_agents_initialized = 57 [ (android.privacy).dest = DEST_AUTOMATIC ];
@@ -594,16 +604,7 @@
}
optional Zen zen = 71;
- message OneHanded {
- option (android.msg_privacy).dest = DEST_EXPLICIT;
-
- optional SettingProto one_handed_mode_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
- optional SettingProto one_handed_mode_timeout = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
- optional SettingProto taps_app_to_exit = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
- }
- optional OneHanded onehanded = 80;
-
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 82;
+ // Next tag = 83;
}
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..0985fea 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -263,6 +263,7 @@
<protected-broadcast android:name="android.hardware.usb.action.USB_PORT_CHANGED" />
<protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
<protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_DETACHED" />
+ <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE" />
<protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
<protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
@@ -5068,7 +5069,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/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 00505ca..f5d7246 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1172,7 +1172,7 @@
<string name="Midnight" msgid="8176019203622191377">"منتصف الليل"</string>
<string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
<string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
- <string name="selectAll" msgid="1532369154488982046">"اختيار الكل"</string>
+ <string name="selectAll" msgid="1532369154488982046">"تحديد الكل"</string>
<string name="cut" msgid="2561199725874745819">"قص"</string>
<string name="copy" msgid="5472512047143665218">"نسخ"</string>
<string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"تعذّر النسخ في الحافظة"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index ce08e43..d312d85 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1152,7 +1152,7 @@
<string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"Capturar imagen con %1$s"</string>
<string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"Capturar imagen"</string>
<string name="alwaysUse" msgid="3153558199076112903">"Usar siempre para esta acción"</string>
- <string name="use_a_different_app" msgid="4987790276170972776">"Utiliza otra aplicación"</string>
+ <string name="use_a_different_app" msgid="4987790276170972776">"Usar otra aplicación"</string>
<string name="clearDefaultHintMsg" msgid="1325866337702524936">"Para borrar los valores predeterminados, accede a Ajustes del sistema > Aplicaciones > Descargadas."</string>
<string name="chooseActivity" msgid="8563390197659779956">"Selecciona una acción"</string>
<string name="chooseUsbActivity" msgid="2096269989990986612">"Elegir una aplicación para el dispositivo USB"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index eb675dd..485f5bf 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1926,7 +1926,7 @@
<string name="time_picker_header_text" msgid="9073802285051516688">"Ezarri ordua"</string>
<string name="time_picker_input_error" msgid="8386271930742451034">"Idatzi balio duen ordu bat"</string>
<string name="time_picker_prompt_label" msgid="303588544656363889">"Idatzi ordua"</string>
- <string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"Aldatu testu modura ordua zehazteko."</string>
+ <string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"Ordua idazteko, aldatu testua idazteko metodora."</string>
<string name="time_picker_radial_mode_description" msgid="1222342577115016953">"Aldatu erloju modura ordua zehazteko."</string>
<string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"Betetze automatikoaren aukerak"</string>
<string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Gorde betetze automatikoarekin erabiltzeko"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 8d9df1f..bd328b5 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -857,7 +857,7 @@
<string name="emergency_calls_only" msgid="3057351206678279851">"Só chamadas de emerxencia"</string>
<string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Bloqueada pola rede"</string>
<string name="lockscreen_sim_puk_locked_message" msgid="6618356415831082174">"A tarxeta SIM está bloqueada con código PUK."</string>
- <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Consulta a guía do usuario ou ponte en contacto co servizo de asistencia ao cliente."</string>
+ <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Consulta a guía para usuarios ou ponte en contacto co servizo de asistencia ao cliente."</string>
<string name="lockscreen_sim_locked_message" msgid="3160196135801185938">"A tarxeta SIM está bloqueada."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="2286497117428409709">"Desbloqueando tarxeta SIM…"</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"Debuxaches incorrectamente o padrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nTéntao de novo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 99665c4..4f3d890 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1548,7 +1548,7 @@
<string name="launchBrowserDefault" msgid="6328349989932924119">"ब्राउज़र लॉन्च करें?"</string>
<string name="SetupCallDefault" msgid="5581740063237175247">"कॉल स्वीकार करें?"</string>
<string name="activity_resolver_use_always" msgid="5575222334666843269">"हमेशा"</string>
- <string name="activity_resolver_use_once" msgid="948462794469672658">"केवल एक बार"</string>
+ <string name="activity_resolver_use_once" msgid="948462794469672658">"सिर्फ़ एक बार"</string>
<string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s वर्क प्रोफ़ाइल का समर्थन नहीं करता"</string>
<string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"टैबलेट"</string>
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"टीवी"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index e74157f..eabf37c 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1152,7 +1152,7 @@
<string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"「%1$s」を使用して画像をキャプチャ"</string>
<string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"画像をキャプチャ"</string>
<string name="alwaysUse" msgid="3153558199076112903">"常にこの操作で使用する"</string>
- <string name="use_a_different_app" msgid="4987790276170972776">"別のアプリの使用"</string>
+ <string name="use_a_different_app" msgid="4987790276170972776">"別のアプリを使用"</string>
<string name="clearDefaultHintMsg" msgid="1325866337702524936">"[システム設定]>[アプリ]>[ダウンロード済み]でデフォルト設定をクリアします。"</string>
<string name="chooseActivity" msgid="8563390197659779956">"操作の選択"</string>
<string name="chooseUsbActivity" msgid="2096269989990986612">"USBデバイス用アプリを選択"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index affbc83..351790d 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1547,7 +1547,7 @@
<string name="sending" msgid="206925243621664438">"Жіберілуде..."</string>
<string name="launchBrowserDefault" msgid="6328349989932924119">"Браузер қосылсын ба?"</string>
<string name="SetupCallDefault" msgid="5581740063237175247">"Қоңырауды қабылдау?"</string>
- <string name="activity_resolver_use_always" msgid="5575222334666843269">"Үнемі"</string>
+ <string name="activity_resolver_use_always" msgid="5575222334666843269">"Әрқашан"</string>
<string name="activity_resolver_use_once" msgid="948462794469672658">"Бір рет қана"</string>
<string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s жұмыс профилін қолдамайды"</string>
<string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"Планшет"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index df8e33d..f1bea56 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -538,7 +538,7 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Колдонмого сүрөт жыйнагыңызды өзгөртүүгө мүмкүнчүлүк берет."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"медиа жыйнагыңыз сакталган жерлерди окуу"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Колдонмого медиа жыйнагыңыз сакталган жерлерди окууга мүмкүнчүлүк берет."</string>
- <string name="biometric_dialog_default_title" msgid="55026799173208210">"Сиз экениңизди ырастаңыз"</string>
+ <string name="biometric_dialog_default_title" msgid="55026799173208210">"Өзүңүздү ырастаңыз"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометрикалык аппарат жеткиликсиз"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Аныктыгын текшерүү жокко чыгарылды"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Таанылган жок"</string>
@@ -1111,7 +1111,7 @@
<string name="inputMethod" msgid="1784759500516314751">"Киргизүү ыкмасы"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Текст боюнча иштер"</string>
<string name="low_internal_storage_view_title" msgid="9024241779284783414">"Сактагычта орун калбай баратат"</string>
- <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Системанын кээ бир функциялары иштебеши мүмкүн"</string>
+ <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Айрым функциялар иштебеши мүмкүн"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Тутумда сактагыч жетишсиз. 250МБ бош орун бар экенин текшерип туруп, өчүрүп күйгүзүңүз."</string>
<string name="app_running_notification_title" msgid="8985999749231486569">"<xliff:g id="APP_NAME">%1$s</xliff:g> иштөөдө"</string>
<string name="app_running_notification_text" msgid="5120815883400228566">"Көбүрөөк маалымат үчүн же колдонмону токтотуш үчүн таптап коюңуз."</string>
@@ -1547,7 +1547,7 @@
<string name="sending" msgid="206925243621664438">"Жөнөтүлүүдө…"</string>
<string name="launchBrowserDefault" msgid="6328349989932924119">"Серепчи иштетилсинби?"</string>
<string name="SetupCallDefault" msgid="5581740063237175247">"Чалуу кабыл алынсынбы?"</string>
- <string name="activity_resolver_use_always" msgid="5575222334666843269">"Дайыма"</string>
+ <string name="activity_resolver_use_always" msgid="5575222334666843269">"Ар дайым"</string>
<string name="activity_resolver_use_once" msgid="948462794469672658">"Бир жолу гана"</string>
<string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s жумуш профилин колдоого албайт"</string>
<string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"Планшет"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index a24db66..b4982eb 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1547,7 +1547,7 @@
<string name="sending" msgid="206925243621664438">"അയയ്ക്കുന്നു…"</string>
<string name="launchBrowserDefault" msgid="6328349989932924119">"ബ്രൗസർ സമാരംഭിക്കണോ?"</string>
<string name="SetupCallDefault" msgid="5581740063237175247">"കോൾ സ്വീകരിക്കണോ?"</string>
- <string name="activity_resolver_use_always" msgid="5575222334666843269">"എല്ലായ്പ്പോഴും"</string>
+ <string name="activity_resolver_use_always" msgid="5575222334666843269">"എല്ലായ്പ്പോഴും"</string>
<string name="activity_resolver_use_once" msgid="948462794469672658">"ഒരിക്കൽ മാത്രം"</string>
<string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s, ഔദ്യോഗിക പ്രൊഫൈലിനെ പിന്തുണയ്ക്കുന്നില്ല"</string>
<string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"ടാബ്ലെറ്റ്"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 8eb8cef..be21f7b 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1093,7 +1093,7 @@
<string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
<string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
<string name="selectAll" msgid="1532369154488982046">"Hammasini belgilash"</string>
- <string name="cut" msgid="2561199725874745819">"Kesish"</string>
+ <string name="cut" msgid="2561199725874745819">"Kesib olish"</string>
<string name="copy" msgid="5472512047143665218">"Nusxa olish"</string>
<string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"Vaqtinchalik xotiraga nusxalab bo‘lmadi"</string>
<string name="paste" msgid="461843306215520225">"Joylash"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 8061ed8..33675da 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -538,7 +538,7 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Cho phép ứng dụng này sửa đổi bộ sưu tập ảnh của bạn."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"đọc vị trí từ bộ sưu tập phương tiện"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Cho phép ứng dụng này đọc vị trí từ bộ sưu tập phương tiện của bạn."</string>
- <string name="biometric_dialog_default_title" msgid="55026799173208210">"Xác minh đó là bạn"</string>
+ <string name="biometric_dialog_default_title" msgid="55026799173208210">"Xác minh danh tính của bạn"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Không có phần cứng sinh trắc học"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Đã hủy xác thực"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Không nhận dạng được"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 51737ba..6ccfc09 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1792,8 +1792,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"已由你的管理員更新"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"已由你的管理員刪除"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"確定"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"為了延長電池續航力,節約耗電量功能會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞\n\n"<annotation id="url">"瞭解詳情"</annotation></string>
- <string name="battery_saver_description" msgid="8587408568232177204">"為了延長電池續航力,節約耗電量功能會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="5997766757551917769">"為了延長電池續航力,省電模式會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞\n\n"<annotation id="url">"瞭解詳情"</annotation></string>
+ <string name="battery_saver_description" msgid="8587408568232177204">"為了延長電池續航力,省電模式會執行以下動作:\n\n•開啟深色主題\n•關閉或限制背景活動、部分視覺效果和其他功能,例如「Hey Google」啟動字詞"</string>
<string name="data_saver_description" msgid="4995164271550590517">"「數據節省模式」可防止部分應用程式在背景收發資料,以節省數據用量。你目前使用的應用程式可以存取資料,但存取頻率可能不如平時高。舉例來說,圖片可能不會自動顯示,在你輕觸後才會顯示。"</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"要開啟數據節省模式嗎?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"開啟"</string>
@@ -2000,9 +2000,9 @@
<string name="notification_feedback_indicator" msgid="663476517711323016">"提供意見"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"日常安排模式資訊通知"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"電池電力可能會在你平常的充電時間前耗盡"</string>
- <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"已啟用節約耗電量模式以延長電池續航力"</string>
- <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"節約耗電量"</string>
- <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"節約耗電量模式已關閉"</string>
+ <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"已啟用省電模式以延長電池續航力"</string>
+ <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"省電模式"</string>
+ <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"省電模式已關閉"</string>
<string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"手機電力充足,各項功能不再受到限制。"</string>
<string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"平板電腦電力充足,各項功能不再受到限制。"</string>
<string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"裝置電力充足,各項功能不再受到限制。"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f60766a..28a164f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -712,10 +712,17 @@
case, this can be disabled (set to false). -->
<bool name="config_enableCarDockHomeLaunch">true</bool>
- <!-- Control whether to force the display of System UI Bars at all times regardless of
- System Ui Flags. This can be useful in the Automotive case if there's a requirement for
- a UI element to be on screen at all times. -->
- <bool name="config_forceShowSystemBars">false</bool>
+ <!-- Control whether to force apps to give up control over the display of system bars at all
+ times regardless of System Ui Flags.
+ In the Automotive case, this is helpful if there's a requirement for an UI element to be on
+ screen at all times. Setting this to true also gives System UI the ability to override the
+ visibility controls for the system through the usage of the
+ "SYSTEM_BAR_VISIBILITY_OVERRIDE" setting.
+ Ex: Only setting the config to true will force show system bars for the entire system.
+ Ex: Setting the config to true and the "SYSTEM_BAR_VISIBILITY_OVERRIDE" setting to
+ "immersive.status=apps" will force show navigation bar for all apps and force hide status
+ bar for all apps. -->
+ <bool name="config_remoteInsetsControllerControlsSystemBars">false</bool>
<!-- HDMI behavior -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 37d1ecc..303fde6 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3024,6 +3024,8 @@
<!-- @hide @TestApi -->
<public type="bool" name="config_assistantOnTopOfDream" id="0x01110005" />
+ <!-- @hide @TestApi -->
+ <public type="bool" name="config_remoteInsetsControllerControlsSystemBars" id="0x01110006" />
<!-- ===============================================================
Resources added in version S of the platform
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5bb784a..16427cd 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1669,7 +1669,7 @@
<java-symbol type="bool" name="config_enableCarDockHomeLaunch" />
<java-symbol type="bool" name="config_enableLockBeforeUnlockScreen" />
<java-symbol type="bool" name="config_enableLockScreenRotation" />
- <java-symbol type="bool" name="config_forceShowSystemBars" />
+ <java-symbol type="bool" name="config_remoteInsetsControllerControlsSystemBars" />
<java-symbol type="bool" name="config_lidControlsScreenLock" />
<java-symbol type="bool" name="config_lidControlsSleep" />
<java-symbol type="bool" name="config_lockDayNightMode" />
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/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
index 17d1389..777f4a3 100644
--- a/core/tests/coretests/src/android/content/ContextTest.java
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -23,6 +23,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.app.ActivityThread;
@@ -180,4 +181,26 @@
VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
return virtualDisplay.getDisplay();
}
+
+ @Test
+ public void testIsUiContext_ContextWrapper() {
+ ContextWrapper wrapper = new ContextWrapper(null /* base */);
+
+ assertFalse(wrapper.isUiContext());
+
+ wrapper = new ContextWrapper(new TestUiContext());
+
+ assertTrue(wrapper.isUiContext());
+ }
+
+ private static class TestUiContext extends ContextWrapper {
+ TestUiContext() {
+ super(null /* base */);
+ }
+
+ @Override
+ public boolean isUiContext() {
+ return true;
+ }
+ }
}
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/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 1b32725..7efd616 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -71,6 +71,9 @@
private SurfaceControl mLeash;
@Mock Transaction mMockTransaction;
private InsetsSource mSpyInsetsSource;
+ private boolean mRemoveSurfaceCalled = false;
+ private InsetsController mController;
+ private InsetsState mState;
@Before
public void setup() {
@@ -89,13 +92,19 @@
} catch (BadTokenException e) {
// activity isn't running, lets ignore BadTokenException.
}
- InsetsState state = new InsetsState();
+ mState = new InsetsState();
mSpyInsetsSource = Mockito.spy(new InsetsSource(ITYPE_STATUS_BAR));
- state.addSource(mSpyInsetsSource);
+ mState.addSource(mSpyInsetsSource);
- mConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, state,
- () -> mMockTransaction,
- new InsetsController(new ViewRootInsetsControllerHost(viewRootImpl)));
+ mController = new InsetsController(new ViewRootInsetsControllerHost(viewRootImpl));
+ mConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, mState,
+ () -> mMockTransaction, mController) {
+ @Override
+ public void removeSurface() {
+ super.removeSurface();
+ mRemoveSurfaceCalled = true;
+ }
+ };
});
instrumentation.waitForIdleSync();
@@ -171,6 +180,25 @@
mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()),
new int[1], hideTypes);
assertEquals(statusBars(), hideTypes[0]);
+ assertFalse(mRemoveSurfaceCalled);
});
}
+
+ @Test
+ public void testRestore_noAnimation() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mConsumer.hide();
+ mController.onStateChanged(mState);
+ mConsumer.setControl(null, new int[1], new int[1]);
+ reset(mMockTransaction);
+ verifyZeroInteractions(mMockTransaction);
+ mRemoveSurfaceCalled = false;
+ int[] hideTypes = new int[1];
+ mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()),
+ new int[1], hideTypes);
+ assertTrue(mRemoveSurfaceCalled);
+ assertEquals(0, hideTypes[0]);
+ });
+
+ }
}
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index 89cc6e7..df2946c 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -37,7 +37,6 @@
import android.app.Activity;
import android.app.Instrumentation;
import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableString;
@@ -96,7 +95,6 @@
mMotionEvents.clear();
}
- @Presubmit
@Test
public void testCursorDrag_horizontal_whenTextViewContentsFitOnScreen() throws Throwable {
String text = "Hello world!";
@@ -145,7 +143,7 @@
// Swipe along a diagonal path. This should drag the cursor. Because we snap the finger to
// the handle as the touch moves downwards (and because we have some slop to avoid jumping
// across lines), the cursor position will end up higher than the finger position.
- onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("3")));
+ onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("2")));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("1")));
// Swipe right-down along a very steep diagonal path. This should not drag the cursor.
@@ -181,7 +179,7 @@
// Swipe along a diagonal path. This should drag the cursor. Because we snap the finger to
// the handle as the touch moves downwards (and because we have some slop to avoid jumping
// across lines), the cursor position will end up higher than the finger position.
- onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("3")));
+ onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("2")));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("1")));
// Swipe right-down along a very steep diagonal path. This should not drag the cursor.
diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
index ec75e40..35fd4bd 100644
--- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
+++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
@@ -326,9 +326,9 @@
mTouchState.update(event1, mConfig);
assertSingleTap(mTouchState, 0f, 0f, 0, 0);
- // Simulate an ACTION_MOVE event that is > 30 deg from vertical.
+ // Simulate an ACTION_MOVE event that is > 45 deg from vertical.
long event2Time = 1002;
- MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 173f);
+ MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 90f);
mTouchState.update(event2, mConfig);
assertDrag(mTouchState, 0f, 0f, 0, 0, false);
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index b7d1c9b..a5117a3 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -807,6 +807,38 @@
}
}
+ @Test
+ public void testNativeTids() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ Binder binder = new Binder();
+
+ bcs.nativeTid = 3;
+
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+
+ bcs.nativeTid = 1;
+
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+
+ bcs.nativeTid = 1;
+
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+
+ bcs.nativeTid = 2;
+
+ callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+
+ int[] tids = bcs.getNativeTids();
+ assertEquals(3, tids.length);
+ assertEquals(1, tids[0]);
+ assertEquals(2, tids[1]);
+ assertEquals(3, tids[2]);
+ }
+
private static class TestHandler extends Handler {
ArrayList<Runnable> mRunnables = new ArrayList<>();
@@ -825,6 +857,7 @@
public int callingUid = CALLING_UID;
public long time = 1234;
public long elapsedTime = 0;
+ public int nativeTid;
TestBinderCallsStats() {
this(mDeviceState);
@@ -874,6 +907,10 @@
protected void setCallingUid(int uid) {
callingUid = uid;
}
- }
+ @Override
+ protected int getNativeTid() {
+ return nativeTid;
+ }
+ }
}
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/data/keyboards/Vendor_1532_Product_1004.kl b/data/keyboards/Vendor_1532_Product_1004.kl
new file mode 100644
index 0000000..bfbfed5
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_1004.kl
@@ -0,0 +1,65 @@
+# 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.
+
+#
+# Razer Raiju Ultimate Edition Controller with wired USB interface.
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130 BUTTON_X
+# Cross
+key 0x131 BUTTON_A
+# Circle
+key 0x132 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x134 BUTTON_L1
+key 0x135 BUTTON_R1
+key 0x136 BUTTON_L2
+key 0x137 BUTTON_R2
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# L2 axis
+axis 0x09 RTRIGGER
+# R2 axis
+axis 0x0a LTRIGGER
+
+# Left stick click
+key 0x13a BUTTON_THUMBL
+# Right stick click
+key 0x13b BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138 BUTTON_SELECT
+# Options
+key 0x139 BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# Touchpad press
+key 0x13d BUTTON_1
diff --git a/data/keyboards/Vendor_1532_Product_1007.kl b/data/keyboards/Vendor_1532_Product_1007.kl
new file mode 100644
index 0000000..6f6c286
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_1007.kl
@@ -0,0 +1,65 @@
+# 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.
+
+#
+# Razer Raiju Tournament Edition Controller with wired USB interface.
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130 BUTTON_X
+# Cross
+key 0x131 BUTTON_A
+# Circle
+key 0x132 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x134 BUTTON_L1
+key 0x135 BUTTON_R1
+key 0x136 BUTTON_L2
+key 0x137 BUTTON_R2
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# L2 axis
+axis 0x09 RTRIGGER
+# R2 axis
+axis 0x0a LTRIGGER
+
+# Left stick click
+key 0x13a BUTTON_THUMBL
+# Right stick click
+key 0x13b BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138 BUTTON_SELECT
+# Options
+key 0x139 BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# Touchpad press
+key 0x13d BUTTON_1
diff --git a/data/keyboards/Vendor_1532_Product_1009.kl b/data/keyboards/Vendor_1532_Product_1009.kl
new file mode 100644
index 0000000..c380d5c3
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_1009.kl
@@ -0,0 +1,65 @@
+# 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.
+
+#
+# Razer Raiju Ultimate Edition Controller with wireless Bluetooth interface.
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130 BUTTON_X
+# Cross
+key 0x131 BUTTON_A
+# Circle
+key 0x132 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x134 BUTTON_L1
+key 0x135 BUTTON_R1
+key 0x136 BUTTON_L2
+key 0x137 BUTTON_R2
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# L2 axis
+axis 0x09 RTRIGGER
+# R2 axis
+axis 0x0a LTRIGGER
+
+# Left stick click
+key 0x13a BUTTON_THUMBL
+# Right stick click
+key 0x13b BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138 BUTTON_SELECT
+# Options
+key 0x139 BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# Touchpad press
+key 0x13d BUTTON_1
diff --git a/data/keyboards/Vendor_1532_Product_100a.kl b/data/keyboards/Vendor_1532_Product_100a.kl
new file mode 100644
index 0000000..b0e966d
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_100a.kl
@@ -0,0 +1,65 @@
+# 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.
+
+#
+# Razer Raiju Tournament Edition Controller with wireless Bluetooth interface.
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130 BUTTON_X
+# Cross
+key 0x131 BUTTON_A
+# Circle
+key 0x132 BUTTON_B
+# Triangle
+key 0x133 BUTTON_Y
+
+key 0x134 BUTTON_L1
+key 0x135 BUTTON_R1
+key 0x136 BUTTON_L2
+key 0x137 BUTTON_R2
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# L2 axis
+axis 0x09 RTRIGGER
+# R2 axis
+axis 0x0a LTRIGGER
+
+# Left stick click
+key 0x13a BUTTON_THUMBL
+# Right stick click
+key 0x13b BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138 BUTTON_SELECT
+# Options
+key 0x139 BUTTON_START
+# PS key
+key 0x13c BUTTON_MODE
+
+# Touchpad press
+key 0x13d BUTTON_1
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/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 32c4173..c701353 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -208,8 +208,12 @@
return config;
}
+extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
+
void EglManager::initExtensions() {
auto extensions = StringUtils::split(eglQueryString(mEglDisplay, EGL_EXTENSIONS));
+ auto extensionsAndroid =
+ StringUtils::split(eglQueryStringImplementationANDROID(mEglDisplay, EGL_EXTENSIONS));
// For our purposes we don't care if EGL_BUFFER_AGE is a result of
// EGL_EXT_buffer_age or EGL_KHR_partial_update as our usage is covered
@@ -228,9 +232,12 @@
EglExtensions.displayP3 = extensions.has("EGL_EXT_gl_colorspace_display_p3_passthrough");
EglExtensions.contextPriority = extensions.has("EGL_IMG_context_priority");
EglExtensions.surfacelessContext = extensions.has("EGL_KHR_surfaceless_context");
- EglExtensions.nativeFenceSync = extensions.has("EGL_ANDROID_native_fence_sync");
EglExtensions.fenceSync = extensions.has("EGL_KHR_fence_sync");
EglExtensions.waitSync = extensions.has("EGL_KHR_wait_sync");
+
+ // EGL_ANDROID_native_fence_sync is not exposed to applications, so access
+ // this through the private Android-specific query instead.
+ EglExtensions.nativeFenceSync = extensionsAndroid.has("EGL_ANDROID_native_fence_sync");
}
bool EglManager::hasEglContext() {
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 3b494e9..5e480a6 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -24,31 +24,10 @@
#include <log/log.h>
+#include <memory>
+
namespace android {
-// --- WeakLooperCallback ---
-
-class WeakLooperCallback: public LooperCallback {
-protected:
- virtual ~WeakLooperCallback() { }
-
-public:
- explicit WeakLooperCallback(const wp<LooperCallback>& callback) :
- mCallback(callback) {
- }
-
- virtual int handleEvent(int fd, int events, void* data) {
- sp<LooperCallback> callback = mCallback.promote();
- if (callback != NULL) {
- return callback->handleEvent(fd, events, data);
- }
- return 0; // the client is gone, remove the callback
- }
-
-private:
- wp<LooperCallback> mCallback;
-};
-
// --- PointerController ---
// Time to wait before starting the fade when the pointer is inactive.
@@ -64,29 +43,50 @@
// The number of events to be read at once for DisplayEventReceiver.
static const int EVENT_BUFFER_SIZE = 100;
-// --- PointerController ---
+std::shared_ptr<PointerController> PointerController::create(
+ const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+ const sp<SpriteController>& spriteController) {
+ std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>(
+ new PointerController(policy, looper, spriteController));
-PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
- const sp<Looper>& looper, const sp<SpriteController>& spriteController) :
- mPolicy(policy), mLooper(looper), mSpriteController(spriteController) {
- mHandler = new WeakMessageHandler(this);
- mCallback = new WeakLooperCallback(this);
+ /*
+ * Now we need to hook up the constructed PointerController object to its callbacks.
+ *
+ * This must be executed after the constructor but before any other methods on PointerController
+ * in order to ensure that the fully constructed object is visible on the Looper thread, since
+ * that may be a different thread than where the PointerController is initially constructed.
+ *
+ * Unfortunately, this cannot be done as part of the constructor since we need to hand out
+ * weak_ptr's which themselves cannot be constructed until there's at least one shared_ptr.
+ */
- if (mDisplayEventReceiver.initCheck() == NO_ERROR) {
- mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK,
- Looper::EVENT_INPUT, mCallback, nullptr);
+ controller->mHandler->pointerController = controller;
+ controller->mCallback->pointerController = controller;
+ if (controller->mDisplayEventReceiver.initCheck() == NO_ERROR) {
+ controller->mLooper->addFd(controller->mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK,
+ Looper::EVENT_INPUT, controller->mCallback, nullptr);
} else {
ALOGE("Failed to initialize DisplayEventReceiver.");
}
+ return controller;
+}
+PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
+ const sp<Looper>& looper,
+ const sp<SpriteController>& spriteController)
+ : mPolicy(policy),
+ mLooper(looper),
+ mSpriteController(spriteController),
+ mHandler(new MessageHandler()),
+ mCallback(new LooperCallback()) {
AutoMutex _l(mLock);
mLocked.animationPending = false;
- mLocked.presentation = PRESENTATION_POINTER;
+ mLocked.presentation = Presentation::POINTER;
mLocked.presentationChanged = false;
- mLocked.inactivityTimeout = INACTIVITY_TIMEOUT_NORMAL;
+ mLocked.inactivityTimeout = InactivityTimeout::NORMAL;
mLocked.pointerFadeDirection = 0;
mLocked.pointerX = 0;
@@ -221,7 +221,7 @@
removeInactivityTimeoutLocked();
// Start fading.
- if (transition == TRANSITION_IMMEDIATE) {
+ if (transition == Transition::IMMEDIATE) {
mLocked.pointerFadeDirection = 0;
mLocked.pointerAlpha = 0.0f;
updatePointerLocked();
@@ -238,7 +238,7 @@
resetInactivityTimeoutLocked();
// Start unfading.
- if (transition == TRANSITION_IMMEDIATE) {
+ if (transition == Transition::IMMEDIATE) {
mLocked.pointerFadeDirection = 0;
mLocked.pointerAlpha = 1.0f;
updatePointerLocked();
@@ -262,7 +262,7 @@
return;
}
- if (presentation == PRESENTATION_POINTER) {
+ if (presentation == Presentation::POINTER) {
if (mLocked.additionalMouseResources.empty()) {
mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
&mLocked.animationResources,
@@ -480,24 +480,35 @@
updatePointerLocked();
}
-void PointerController::handleMessage(const Message& message) {
+void PointerController::MessageHandler::handleMessage(const Message& message) {
+ std::shared_ptr<PointerController> controller = pointerController.lock();
+
+ if (controller == nullptr) {
+ ALOGE("PointerController instance was released before processing message: what=%d",
+ message.what);
+ return;
+ }
switch (message.what) {
case MSG_INACTIVITY_TIMEOUT:
- doInactivityTimeout();
+ controller->doInactivityTimeout();
break;
}
}
-int PointerController::handleEvent(int /* fd */, int events, void* /* data */) {
+int PointerController::LooperCallback::handleEvent(int /* fd */, int events, void* /* data */) {
+ std::shared_ptr<PointerController> controller = pointerController.lock();
+ if (controller == nullptr) {
+ ALOGW("PointerController instance was released with pending callbacks. events=0x%x",
+ events);
+ return 0; // Remove the callback, the PointerController is gone anyways
+ }
if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
- ALOGE("Display event receiver pipe was closed or an error occurred. "
- "events=0x%x", events);
+ ALOGE("Display event receiver pipe was closed or an error occurred. events=0x%x", events);
return 0; // remove the callback
}
if (!(events & Looper::EVENT_INPUT)) {
- ALOGW("Received spurious callback for unhandled poll event. "
- "events=0x%x", events);
+ ALOGW("Received spurious callback for unhandled poll event. events=0x%x", events);
return 1; // keep the callback
}
@@ -505,7 +516,7 @@
ssize_t n;
nsecs_t timestamp;
DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
- while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+ while ((n = controller->mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
for (size_t i = 0; i < static_cast<size_t>(n); ++i) {
if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
timestamp = buf[i].header.timestamp;
@@ -514,7 +525,7 @@
}
}
if (gotVsync) {
- doAnimate(timestamp);
+ controller->doAnimate(timestamp);
}
return 1; // keep the callback
}
@@ -613,7 +624,7 @@
}
void PointerController::doInactivityTimeout() {
- fade(TRANSITION_GRADUAL);
+ fade(Transition::GRADUAL);
}
void PointerController::startAnimationLocked() {
@@ -627,8 +638,9 @@
void PointerController::resetInactivityTimeoutLocked() {
mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
- nsecs_t timeout = mLocked.inactivityTimeout == INACTIVITY_TIMEOUT_SHORT
- ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL;
+ nsecs_t timeout = mLocked.inactivityTimeout == InactivityTimeout::SHORT
+ ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT
+ : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL;
mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT);
}
@@ -655,7 +667,7 @@
}
if (mLocked.pointerIconChanged || mLocked.presentationChanged) {
- if (mLocked.presentation == PRESENTATION_POINTER) {
+ if (mLocked.presentation == Presentation::POINTER) {
if (mLocked.requestedPointerType == mPolicy->getDefaultPointerIconId()) {
mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
} else {
@@ -731,7 +743,7 @@
return spot;
}
}
- return NULL;
+ return nullptr;
}
void PointerController::releaseSpotLocked(Spot* spot) {
@@ -772,7 +784,7 @@
mLocked.additionalMouseResources.clear();
mLocked.animationResources.clear();
- if (mLocked.presentation == PRESENTATION_POINTER) {
+ if (mLocked.presentation == Presentation::POINTER) {
mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
&mLocked.animationResources, mLocked.viewport.displayId);
}
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index ebc622b..14c0679 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -17,19 +17,20 @@
#ifndef _UI_POINTER_CONTROLLER_H
#define _UI_POINTER_CONTROLLER_H
-#include "SpriteController.h"
-
-#include <map>
-#include <vector>
-
-#include <ui/DisplayInfo.h>
+#include <PointerControllerInterface.h>
+#include <gui/DisplayEventReceiver.h>
#include <input/DisplayViewport.h>
#include <input/Input.h>
-#include <PointerControllerInterface.h>
+#include <ui/DisplayInfo.h>
#include <utils/BitSet.h>
-#include <utils/RefBase.h>
#include <utils/Looper.h>
-#include <gui/DisplayEventReceiver.h>
+#include <utils/RefBase.h>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "SpriteController.h"
namespace android {
@@ -70,25 +71,22 @@
virtual int32_t getCustomPointerIconId() = 0;
};
-
/*
* Tracks pointer movements and draws the pointer sprite to a surface.
*
* Handles pointer acceleration and animation.
*/
-class PointerController : public PointerControllerInterface, public MessageHandler,
- public LooperCallback {
-protected:
- virtual ~PointerController();
-
+class PointerController : public PointerControllerInterface {
public:
- enum InactivityTimeout {
- INACTIVITY_TIMEOUT_NORMAL = 0,
- INACTIVITY_TIMEOUT_SHORT = 1,
+ static std::shared_ptr<PointerController> create(
+ const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+ const sp<SpriteController>& spriteController);
+ enum class InactivityTimeout {
+ NORMAL = 0,
+ SHORT = 1,
};
- PointerController(const sp<PointerControllerPolicyInterface>& policy,
- const sp<Looper>& looper, const sp<SpriteController>& spriteController);
+ virtual ~PointerController();
virtual bool getBounds(float* outMinX, float* outMinY,
float* outMaxX, float* outMaxY) const;
@@ -113,8 +111,8 @@
void reloadPointerResources();
private:
- static const size_t MAX_RECYCLED_SPRITES = 12;
- static const size_t MAX_SPOTS = 12;
+ static constexpr size_t MAX_RECYCLED_SPRITES = 12;
+ static constexpr size_t MAX_SPOTS = 12;
enum {
MSG_INACTIVITY_TIMEOUT,
@@ -130,8 +128,13 @@
float x, y;
inline Spot(uint32_t id, const sp<Sprite>& sprite)
- : id(id), sprite(sprite), alpha(1.0f), scale(1.0f),
- x(0.0f), y(0.0f), lastIcon(NULL) { }
+ : id(id),
+ sprite(sprite),
+ alpha(1.0f),
+ scale(1.0f),
+ x(0.0f),
+ y(0.0f),
+ lastIcon(nullptr) {}
void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId);
@@ -139,12 +142,24 @@
const SpriteIcon* lastIcon;
};
+ class MessageHandler : public virtual android::MessageHandler {
+ public:
+ void handleMessage(const Message& message) override;
+ std::weak_ptr<PointerController> pointerController;
+ };
+
+ class LooperCallback : public virtual android::LooperCallback {
+ public:
+ int handleEvent(int fd, int events, void* data) override;
+ std::weak_ptr<PointerController> pointerController;
+ };
+
mutable Mutex mLock;
sp<PointerControllerPolicyInterface> mPolicy;
sp<Looper> mLooper;
sp<SpriteController> mSpriteController;
- sp<WeakMessageHandler> mHandler;
+ sp<MessageHandler> mHandler;
sp<LooperCallback> mCallback;
DisplayEventReceiver mDisplayEventReceiver;
@@ -181,14 +196,15 @@
int32_t buttonState;
std::map<int32_t /* displayId */, std::vector<Spot*>> spotsByDisplay;
- std::vector<sp<Sprite> > recycledSprites;
+ std::vector<sp<Sprite>> recycledSprites;
} mLocked GUARDED_BY(mLock);
+ PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+ const sp<SpriteController>& spriteController);
+
bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
void setPositionLocked(float x, float y);
- void handleMessage(const Message& message);
- int handleEvent(int fd, int events, void* data);
void doAnimate(nsecs_t timestamp);
bool doFadingAnimationLocked(nsecs_t timestamp);
bool doBitmapAnimationLocked(nsecs_t timestamp);
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index a157426..6e129a0 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -136,7 +136,7 @@
sp<MockSprite> mPointerSprite;
sp<MockPointerControllerPolicyInterface> mPolicy;
sp<MockSpriteController> mSpriteController;
- sp<PointerController> mPointerController;
+ std::shared_ptr<PointerController> mPointerController;
private:
void loopThread();
@@ -160,7 +160,7 @@
EXPECT_CALL(*mSpriteController, createSprite())
.WillOnce(Return(mPointerSprite));
- mPointerController = new PointerController(mPolicy, mLooper, mSpriteController);
+ mPointerController = PointerController::create(mPolicy, mLooper, mSpriteController);
}
PointerControllerTest::~PointerControllerTest() {
@@ -193,7 +193,7 @@
TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
ensureDisplayViewportIsSet();
- mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+ mPointerController->unfade(PointerController::Transition::IMMEDIATE);
std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
EXPECT_CALL(*mPointerSprite, setVisible(true));
@@ -208,7 +208,7 @@
TEST_F(PointerControllerTest, updatePointerIcon) {
ensureDisplayViewportIsSet();
- mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+ mPointerController->unfade(PointerController::Transition::IMMEDIATE);
int32_t type = CURSOR_TYPE_ADDITIONAL;
std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
@@ -224,7 +224,7 @@
TEST_F(PointerControllerTest, setCustomPointerIcon) {
ensureDisplayViewportIsSet();
- mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+ mPointerController->unfade(PointerController::Transition::IMMEDIATE);
int32_t style = CURSOR_TYPE_CUSTOM;
float hotSpotX = 15;
@@ -246,13 +246,13 @@
}
TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
- mPointerController->setPresentation(PointerController::PRESENTATION_POINTER);
+ mPointerController->setPresentation(PointerController::Presentation::POINTER);
mPointerController->setSpots(nullptr, nullptr, BitSet32(), -1);
mPointerController->clearSpots();
mPointerController->setPosition(1.0f, 1.0f);
mPointerController->move(1.0f, 1.0f);
- mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
- mPointerController->fade(PointerController::TRANSITION_IMMEDIATE);
+ mPointerController->unfade(PointerController::Transition::IMMEDIATE);
+ mPointerController->fade(PointerController::Transition::IMMEDIATE);
EXPECT_TRUE(mPolicy->noResourcesAreLoaded());
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..c0b8e1b 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;
/**
@@ -264,7 +260,9 @@
* {@code OP_MONITOR_HIGH_POWER_LOCATION}.
*
* @hide
+ * @deprecated This action is unnecessary from Android S forward.
*/
+ @Deprecated
public static final String HIGH_POWER_REQUEST_CHANGE_ACTION =
"android.location.HIGH_POWER_REQUEST_CHANGE";
@@ -305,11 +303,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 +642,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 +739,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 +783,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 +815,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 +842,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 +870,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 +1180,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 +1226,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 +1287,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;
- }
}
/**
@@ -1865,7 +1857,8 @@
"GpsStatus APIs not supported, please use GnssStatus APIs instead");
}
- getGnssStatusTransportMultiplexer().addListener(listener, DIRECT_EXECUTOR);
+ getGnssStatusTransportMultiplexer().addListener(listener,
+ new HandlerExecutor(new Handler()));
return true;
}
@@ -1984,7 +1977,7 @@
@Deprecated
@RequiresPermission(ACCESS_FINE_LOCATION)
public boolean addNmeaListener(@NonNull OnNmeaMessageListener listener) {
- return addNmeaListener(DIRECT_EXECUTOR, listener);
+ return addNmeaListener(listener, null);
}
/**
@@ -2397,12 +2390,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 +2401,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 +2424,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/MediaFrameworkInitializer.java b/media/java/android/media/MediaFrameworkInitializer.java
new file mode 100644
index 0000000..577442e
--- /dev/null
+++ b/media/java/android/media/MediaFrameworkInitializer.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.NonNull;
+import android.app.SystemServiceRegistry;
+import android.content.Context;
+import android.media.session.MediaSessionManager;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Class for performing registration for all media services
+ *
+ * TODO (b/160513103): Move this class when moving media service code to APEX
+ * @hide
+ */
+public class MediaFrameworkInitializer {
+ private MediaFrameworkInitializer() {
+ }
+
+ private static volatile MediaServiceManager sMediaServiceManager;
+
+ /**
+ * Sets an instance of {@link MediaServiceManager} that allows
+ * the media mainline module to register/obtain media binder services. This is called
+ * by the platform during the system initialization.
+ *
+ * @param mediaServiceManager instance of {@link MediaServiceManager} that allows
+ * the media mainline module to register/obtain media binder services.
+ */
+ public static void setMediaServiceManager(
+ @NonNull MediaServiceManager mediaServiceManager) {
+ Preconditions.checkState(sMediaServiceManager == null,
+ "setMediaServiceManager called twice!");
+ sMediaServiceManager = Objects.requireNonNull(mediaServiceManager);
+ }
+
+ /** @hide */
+ public static MediaServiceManager getMediaServiceManager() {
+ return sMediaServiceManager;
+ }
+
+ /**
+ * Called by {@link SystemServiceRegistry}'s static initializer and registers all media
+ * services to {@link Context}, so that {@link Context#getSystemService} can return them.
+ *
+ * @throws IllegalStateException if this is called from anywhere besides
+ * {@link SystemServiceRegistry}
+ */
+ public static void registerServiceWrappers() {
+ SystemServiceRegistry.registerContextAwareService(
+ Context.MEDIA_SESSION_SERVICE,
+ MediaSessionManager.class,
+ context -> new MediaSessionManager(context)
+ );
+ }
+}
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/java/android/media/MediaServiceManager.java b/media/java/android/media/MediaServiceManager.java
new file mode 100644
index 0000000..21e2d84
--- /dev/null
+++ b/media/java/android/media/MediaServiceManager.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.os.ServiceManager;
+
+/**
+ * Provides a way to register and obtain the system service binder objects managed by the media
+ * service.
+ *
+ * <p> Only the media mainline module will be able to access an instance of this class.
+ * @hide
+ */
+public class MediaServiceManager {
+ /**
+ * @hide
+ */
+ public MediaServiceManager() {}
+
+ /**
+ * A class that exposes the methods to register and obtain each system service.
+ */
+ public static final class ServiceRegisterer {
+ private final String mServiceName;
+
+ /**
+ * @hide
+ */
+ public ServiceRegisterer(String serviceName) {
+ mServiceName = serviceName;
+ }
+
+ /**
+ * Get the system server binding object for MediaServiceManager.
+ *
+ * <p> This blocks until the service instance is ready.
+ * or a timeout happens, in which case it returns null.
+ */
+ @Nullable
+ public IBinder get() {
+ return ServiceManager.getService(mServiceName);
+ }
+ }
+
+ /**
+ * Returns {@link ServiceRegisterer} for the "media_session" service.
+ */
+ @NonNull
+ public ServiceRegisterer getMediaSessionServiceRegisterer() {
+ return new ServiceRegisterer("media_session");
+ }
+}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 2fd721e..6976a35 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -28,14 +28,13 @@
import android.content.pm.ParceledListSlice;
import android.media.AudioManager;
import android.media.IRemoteVolumeController;
+import android.media.MediaFrameworkInitializer;
import android.media.MediaSession2;
import android.media.Session2Token;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.service.media.MediaBrowserService;
import android.service.notification.NotificationListenerService;
@@ -115,8 +114,10 @@
// Consider rewriting like DisplayManagerGlobal
// Decide if we need context
mContext = context;
- IBinder b = ServiceManager.getService(Context.MEDIA_SESSION_SERVICE);
- mService = ISessionManager.Stub.asInterface(b);
+ mService = ISessionManager.Stub.asInterface(MediaFrameworkInitializer
+ .getMediaServiceManager()
+ .getMediaSessionServiceRegisterer()
+ .get());
}
/**
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 515d610..df022d5 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -447,7 +447,7 @@
if (mediaEvent.avMemory.getNativeHandle() != NULL || mediaEvent.avDataId != 0) {
sp<MediaEvent> mediaEventSp =
new MediaEvent(mIFilter, mediaEvent.avMemory,
- mediaEvent.avDataId, dataLength, obj);
+ mediaEvent.avDataId, dataLength + offset, obj);
mediaEventSp->mAvHandleRefCnt++;
env->SetLongField(obj, eventContext, (jlong) mediaEventSp.get());
mediaEventSp->incStrong(obj);
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/media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh b/media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh
index b709c5f..43db353 100644
--- a/media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh
+++ b/media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh
@@ -13,9 +13,6 @@
mm
-# Push the files onto the device.
-. $ANDROID_BUILD_TOP/frameworks/av/media/libmediatranscoding/tests/assets/push_assets.sh
-
echo "[==========] waiting for device"
adb root && adb wait-for-device remount
@@ -27,6 +24,13 @@
mmm -j16 .
adb install -r -g ${OUT}/testcases/mediatranscodingtest/arm64/mediatranscodingtest.apk
+# Push the files into app's cache directory/
+FILES=$ANDROID_BUILD_TOP/frameworks/av/media/libmediatranscoding/tests/assets/*
+for file in $FILES
+do
+adb push --sync $file /data/user/0/com.android.mediatranscodingtest/cache/
+done
+
echo "[==========] running real transcoding tests"
adb shell am instrument -e class com.android.mediatranscodingtest.MediaTranscodeManagerTest -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
index e5952fb..3a5e692 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
@@ -67,7 +67,7 @@
// Setting for transcoding to H.264.
private static final String MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC;
- private static final int BIT_RATE = 2000000; // 2Mbps
+ private static final int BIT_RATE = 20000000; // 20Mbps
private static final int WIDTH = 1920;
private static final int HEIGHT = 1080;
@@ -177,7 +177,7 @@
assertNotNull(job);
if (job != null) {
- Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to complete.");
+ Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to cancel.");
boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertTrue("Transcode failed to complete in time.", finishedOnTime);
@@ -192,5 +192,43 @@
stats.mAveragePSNR >= PSNR_THRESHOLD);
}
+ @Test
+ public void testCancelTranscoding() throws Exception {
+ Log.d(TAG, "Starting: testMediaTranscodeManager");
+ Semaphore transcodeCompleteSemaphore = new Semaphore(0);
+
+ // Transcode a 15 seconds video, so that the transcoding is not finished when we cancel.
+ Uri srcUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
+ + mContext.getCacheDir().getAbsolutePath() + "/longtest_15s.mp4");
+ Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
+ + mContext.getCacheDir().getAbsolutePath() + "/HevcTranscode.mp4");
+
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
+ .setSourceUri(srcUri)
+ .setDestinationUri(destinationUri)
+ .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+ .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+ .setVideoTrackFormat(createMediaFormat())
+ .build();
+ Executor listenerExecutor = Executors.newSingleThreadExecutor();
+
+ TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
+ transcodingJob -> {
+ Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult());
+ assertEquals(transcodingJob.getResult(), TranscodingJob.RESULT_CANCELED);
+ transcodeCompleteSemaphore.release();
+ });
+ assertNotNull(job);
+
+ // TODO(hkuang): Wait for progress update before calling cancel to make sure transcoding is
+ // started.
+
+ job.cancel();
+ Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to cancel.");
+ boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
+ 30, TimeUnit.MILLISECONDS);
+ assertTrue("Fails to cancel transcoding", finishedOnTime);
+ }
}
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
new file mode 100644
index 0000000..ee06720
--- /dev/null
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
@@ -0,0 +1,182 @@
+/*
+ * 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.mediatranscodingtest;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.media.MediaFormat;
+import android.media.MediaTranscodeManager;
+import android.media.MediaTranscodeManager.TranscodingJob;
+import android.media.MediaTranscodeManager.TranscodingRequest;
+import android.net.Uri;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/*
+ * Benchmarking for MediaTranscodeManager in the media framework.
+ *
+ * Note: This benchmarking requires to push all the files from http://go/transcodingbenchmark
+ * to /data/user/0/com.android.mediatranscodingtest/cache/ directory after installing the apk.
+ *
+ * TODO(hkuang): Change it to download from server automatically instead of manually.
+ *
+ * To run this test suite:
+ make frameworks/base/media/tests/MediaTranscodingTest
+ make mediatranscodingtest
+
+ adb install -r testcases/mediatranscodingtest/arm64/mediatranscodingtest.apk
+ // Push the files to /data/user/0/com.android.mediatranscodingtest/cache/
+ adb push $DOWNLOADPATH/*.mp4 /data/user/0/com.android.mediatranscodingtest/cache/
+
+ adb shell am instrument -e class \
+ com.android.mediatranscodingtest.MediaTranscodingBenchmark \
+ -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner
+ *
+ */
+public class MediaTranscodingBenchmark
+ extends ActivityInstrumentationTestCase2<MediaTranscodingTest> {
+ private static final String TAG = "MediaTranscodingBenchmark";
+ // TODO(hkuang): Change this to query from MediaCodecInfo.CodecCapabilities for different
+ // resolution.
+ private static final int MINIMUM_TRANSCODING_FPS = 80;
+ private static final int LOOP_COUNT = 10;
+ // Default Setting for transcoding to H.264.
+ private static final String MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC;
+ private static final int BIT_RATE = 20000000; // 20Mbps
+ private static final int WIDTH = 1920;
+ private static final int HEIGHT = 1080;
+ private Context mContext;
+ private MediaTranscodeManager mMediaTranscodeManager = null;
+
+ public MediaTranscodingBenchmark() {
+ super("com.android.MediaTranscodingBenchmark", MediaTranscodingTest.class);
+ }
+
+ /**
+ * Creates a MediaFormat with the basic set of values.
+ */
+ private static MediaFormat createMediaFormat() {
+ MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, WIDTH, HEIGHT);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
+ return format;
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ Log.d(TAG, "setUp");
+ super.setUp();
+ mContext = getInstrumentation().getContext();
+ mMediaTranscodeManager = MediaTranscodeManager.getInstance(mContext);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /*
+ * Transcode the sourceFileName to destinationFileName with LOOP_COUNT.
+ */
+ private void transcode(final String sourceFileName, final String destinationFileName)
+ throws IOException, InterruptedException {
+ AtomicLong totalTimeMs = new AtomicLong();
+ AtomicLong transcodingTime = new AtomicLong();
+ Uri srcUri = getUri(sourceFileName);
+ Uri dstUri = getUri(destinationFileName);
+
+ MediaTranscodingTestUtil.VideoFileInfo info =
+ MediaTranscodingTestUtil.extractVideoFileInfo(mContext, getUri(sourceFileName));
+ int timeoutSeconds = calMaxTranscodingWaitTimeSeconds(info.mNumVideoFrames,
+ MINIMUM_TRANSCODING_FPS);
+ Log.d(TAG, "Start Transcoding " + info.toString() + " " + timeoutSeconds);
+
+ for (int loop = 0; loop < LOOP_COUNT; ++loop) {
+ Semaphore transcodeCompleteSemaphore = new Semaphore(0);
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
+ .setSourceUri(srcUri)
+ .setDestinationUri(dstUri)
+ .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+ .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+ .setVideoTrackFormat(createMediaFormat())
+ .build();
+ Executor listenerExecutor = Executors.newSingleThreadExecutor();
+
+ long startTimeMs = System.currentTimeMillis();
+ TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
+ transcodingJob -> {
+ Log.d(TAG,
+ "Transcoding completed with result: " + transcodingJob.getResult());
+ assertEquals(transcodingJob.getResult(), TranscodingJob.RESULT_SUCCESS);
+ transcodeCompleteSemaphore.release();
+ transcodingTime.set(System.currentTimeMillis() - startTimeMs);
+ totalTimeMs.addAndGet(transcodingTime.get());
+ });
+
+ if (job != null) {
+ Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to complete.");
+ boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
+ timeoutSeconds, TimeUnit.SECONDS);
+ assertTrue("Transcode failed to complete in time.", finishedOnTime);
+ }
+ Log.i(TAG, "Loop: " + loop + " take " + transcodingTime.get() + " ms ");
+ }
+
+ float fps = info.mNumVideoFrames * 1000 * LOOP_COUNT / totalTimeMs.get();
+ Log.i(TAG, "Transcoding " + info.toString() + " Transcoding fps: " + fps);
+ }
+
+ // Calculate the maximum wait time based on minimum transcoding throughput and frame number.
+ private int calMaxTranscodingWaitTimeSeconds(int numberFrames, int minTranscodingFps) {
+ int waitTimeSeconds = numberFrames / minTranscodingFps;
+ // If waitTimeSeconds is 0, wait for 1 seconds at least.
+ return waitTimeSeconds == 0 ? 1 : waitTimeSeconds;
+ }
+
+ private Uri getUri(final String fileName) {
+ String path = mContext.getCacheDir().getAbsolutePath();
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_FILE).appendPath(path).appendPath(
+ fileName).build();
+ }
+
+ @Test
+ public void testBenchmarkingAVCToAVCWith66FramesWithoutAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_66frame_h264_22Mbps_30fps";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ }
+
+ @Test
+ public void testBenchmarkingAVCToAVCWith66FramesWithAudio() throws Exception {
+ String videoNameWithoutExtension = "video_1920x1080_66frame_h264_22Mbps_30fps_aac";
+ String testVideoName = videoNameWithoutExtension + ".mp4";
+ String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
+
+ transcode(testVideoName, transcodedVideoName);
+ }
+}
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java
index 3b044c7..53b2392 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java
@@ -38,6 +38,7 @@
TestSuite suite = new InstrumentationTestSuite(this);
suite.addTestSuite(MediaTranscodeManagerTest.class);
suite.addTestSuite(MediaTranscodeManagerWithMockServiceTest.class);
+ suite.addTestSuite(MediaTranscodingBenchmark.class);
return suite;
}
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestUtil.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestUtil.java
index a1c3251..69f124f 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestUtil.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestUtil.java
@@ -27,6 +27,7 @@
import android.media.MediaCodecInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
+import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.util.Log;
import android.util.Size;
@@ -42,6 +43,75 @@
/* package */ class MediaTranscodingTestUtil {
private static final String TAG = "MediaTranscodingTestUtil";
+ // Helper class to extract the information from source file and transcoded file.
+ static class VideoFileInfo {
+ String mUri;
+ int mNumVideoFrames = 0;
+ int mWidth = 0;
+ int mHeight = 0;
+ float mVideoFrameRate = 0.0f;
+ boolean mHasAudio = false;
+
+ public String toString() {
+ String str = mUri;
+ str += " Width:" + mWidth;
+ str += " Height:" + mHeight;
+ str += " FrameRate:" + mWidth;
+ str += " FrameCount:" + mNumVideoFrames;
+ str += " HasAudio:" + (mHasAudio ? "Yes" : "No");
+ return str;
+ }
+ }
+
+ static VideoFileInfo extractVideoFileInfo(Context ctx, Uri videoUri) throws IOException {
+ VideoFileInfo info = new VideoFileInfo();
+ AssetFileDescriptor afd = null;
+ MediaMetadataRetriever retriever = null;
+
+ try {
+ afd = ctx.getContentResolver().openAssetFileDescriptor(videoUri, "r");
+ retriever = new MediaMetadataRetriever();
+ retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+
+ info.mUri = videoUri.getLastPathSegment();
+ Log.i(TAG, "Trying to transcode to " + info.mUri);
+ String width = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
+ String height = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
+ if (width != null && height != null) {
+ info.mWidth = Integer.parseInt(width);
+ info.mHeight = Integer.parseInt(height);
+ }
+
+ String frameRate = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE);
+ if (frameRate != null) {
+ info.mVideoFrameRate = Float.parseFloat(frameRate);
+ }
+
+ String frameCount = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT);
+ if (frameCount != null) {
+ info.mNumVideoFrames = Integer.parseInt(frameCount);
+ }
+
+ String hasAudio = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_HAS_AUDIO);
+ if (hasAudio != null) {
+ info.mHasAudio = hasAudio.equals("yes");
+ }
+ } finally {
+ if (retriever != null) {
+ retriever.close();
+ }
+ if (afd != null) {
+ afd.close();
+ }
+ }
+ return info;
+ }
+
static VideoTranscodingStatistics computeStats(final Context ctx, final Uri sourceMp4,
final Uri transcodedMp4)
throws Exception {
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index 53499f8..b693b0e 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();
@@ -3613,8 +3614,13 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void grantPermission(android.hardware.usb.UsbDevice, String);
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void resetUsbGadget();
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long);
+ field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_ACCESSORY_HANDSHAKE = "android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE";
field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE";
+ field public static final String EXTRA_ACCESSORY_HANDSHAKE_END = "android.hardware.usb.extra.ACCESSORY_HANDSHAKE_END";
+ field public static final String EXTRA_ACCESSORY_START = "android.hardware.usb.extra.ACCESSORY_START";
+ field public static final String EXTRA_ACCESSORY_STRING_COUNT = "android.hardware.usb.extra.ACCESSORY_STRING_COUNT";
+ field public static final String EXTRA_ACCESSORY_UEVENT_TIME = "android.hardware.usb.extra.ACCESSORY_UEVENT_TIME";
field public static final long FUNCTION_ACCESSORY = 2L; // 0x2L
field public static final long FUNCTION_ADB = 1L; // 0x1L
field public static final long FUNCTION_AUDIO_SOURCE = 64L; // 0x40L
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 32b33a7..8598f74e 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -49,7 +49,7 @@
"androidx.lifecycle_lifecycle-extensions",
"SystemUI-tags",
"SystemUI-proto",
- "dagger2-2.19",
+ "dagger2",
"//external/kotlinc:kotlin-annotations",
],
@@ -59,7 +59,7 @@
manifest: "AndroidManifest.xml",
- plugins: ["dagger2-compiler-2.19"],
+ plugins: ["dagger2-compiler"],
}
@@ -104,7 +104,7 @@
"mockito-target-inline-minus-junit4",
"testables",
"truth-prebuilt",
- "dagger2-2.19",
+ "dagger2",
"//external/kotlinc:kotlin-annotations",
],
libs: [
@@ -118,7 +118,7 @@
"com.android.systemui",
],
- plugins: ["dagger2-compiler-2.19"],
+ plugins: ["dagger2-compiler"],
}
android_app {
@@ -157,7 +157,7 @@
kotlincflags: ["-Xjvm-default=enable"],
- plugins: ["dagger2-compiler-2.19"],
+ plugins: ["dagger2-compiler"],
required: ["privapp_whitelist_com.android.systemui"],
}
diff --git a/packages/CarSystemUI/res/layout/headsup_container_bottom.xml b/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
index 1782d25..5aab0a1 100644
--- a/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
+++ b/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
@@ -29,13 +29,12 @@
android:orientation="horizontal"
app:layout_constraintGuide_begin="@dimen/headsup_scrim_height"/>
- <!-- Include a FocusParkingView at the beginning or end. The rotary controller "parks" the
- focus here when the user navigates to another window. This is also used to prevent
- wrap-around which is why it must be first or last in Tab order. -->
+ <!-- Include a FocusParkingView at the beginning. The rotary controller "parks" the focus here
+ when the user navigates to another window. This is also used to prevent wrap-around. -->
<com.android.car.ui.FocusParkingView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<View
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 5bf989a9..4967426 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -67,6 +67,8 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.volume.VolumeDialogComponent;
+import com.android.systemui.wm.DisplayImeController;
+import com.android.systemui.wm.DisplaySystemBarsController;
import javax.inject.Named;
import javax.inject.Singleton;
@@ -97,6 +99,10 @@
groupManager, configurationController);
}
+ @Binds
+ abstract DisplayImeController bindDisplayImeController(
+ DisplaySystemBarsController displaySystemBarsController);
+
@Singleton
@Provides
@Named(LEAK_REPORT_EMAIL_NAME)
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
index d60bc41..adf8d4d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
@@ -148,10 +148,9 @@
CarNavigationBarView view = (CarNavigationBarView) View.inflate(mContext, barLayout,
/* root= */ null);
- // Include a FocusParkingView at the end. The rotary controller "parks" the focus here when
- // the user navigates to another window. This is also used to prevent wrap-around which is
- // why it must be first or last in Tab order.
- view.addView(new FocusParkingView(mContext));
+ // Include a FocusParkingView at the beginning. The rotary controller "parks" the focus here
+ // when the user navigates to another window. This is also used to prevent wrap-around.
+ view.addView(new FocusParkingView(mContext), 0);
mCachedViewMap.put(type, view);
return mCachedViewMap.get(type);
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java
index a423003..13f2b7e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/DummyNotificationShadeWindowController.java
@@ -23,6 +23,7 @@
import com.android.systemui.car.window.SystemUIOverlayWindowController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -49,12 +50,14 @@
DozeParameters dozeParameters,
StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
+ KeyguardViewMediator keyguardViewMediator,
KeyguardBypassController keyguardBypassController,
SysuiColorExtractor colorExtractor,
DumpManager dumpManager,
SystemUIOverlayWindowController overlayWindowController) {
super(context, windowManager, activityManager, dozeParameters, statusBarStateController,
- configurationController, keyguardBypassController, colorExtractor, dumpManager);
+ configurationController, keyguardViewMediator, keyguardBypassController,
+ colorExtractor, dumpManager);
mOverlayWindowController = overlayWindowController;
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java b/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java
new file mode 100644
index 0000000..5f9665f
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/wm/BarControlPolicy.java
@@ -0,0 +1,250 @@
+/*
+ * 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.wm;
+
+import android.car.settings.CarSettings;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.view.WindowInsets;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Util class to load PolicyControl and allow for querying if a package matches immersive filters.
+ * Similar to {@link com.android.server.wm.PolicyControl}, but separate due to CarSystemUI needing
+ * to set its own policies for system bar visibilities.
+ *
+ * This forces immersive mode behavior for one or both system bars (based on a package
+ * list).
+ *
+ * Control by setting {@link Settings.Global#POLICY_CONTROL_AUTO} to one or more name-value pairs.
+ * e.g.
+ * to force immersive mode everywhere:
+ * "immersive.full=*"
+ * to force hide status bars for com.package1 but not com.package2:
+ * "immersive.status=com.package1,-com.package2"
+ *
+ * Separate multiple name-value pairs with ':'
+ * e.g. "immersive.status=com.package:immersive.navigation=*"
+ */
+public class BarControlPolicy {
+
+ private static final String TAG = "BarControlPolicy";
+ private static final boolean DEBUG = false;
+
+ private static final String NAME_IMMERSIVE_FULL = "immersive.full";
+ private static final String NAME_IMMERSIVE_STATUS = "immersive.status";
+ private static final String NAME_IMMERSIVE_NAVIGATION = "immersive.navigation";
+
+ @VisibleForTesting
+ static String sSettingValue;
+ @VisibleForTesting
+ static Filter sImmersiveStatusFilter;
+ private static Filter sImmersiveNavigationFilter;
+
+ /** Loads values from the POLICY_CONTROL setting to set filters. */
+ static boolean reloadFromSetting(Context context) {
+ if (DEBUG) Slog.d(TAG, "reloadFromSetting()");
+ String value = null;
+ try {
+ value = Settings.Global.getStringForUser(context.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ UserHandle.USER_CURRENT);
+ if (sSettingValue == value || sSettingValue != null && sSettingValue.equals(value)) {
+ return false;
+ }
+ setFilters(value);
+ sSettingValue = value;
+ } catch (Throwable t) {
+ Slog.w(TAG, "Error loading policy control, value=" + value, t);
+ return false;
+ }
+ return true;
+ }
+
+ /** Used in testing to reset BarControlPolicy. */
+ @VisibleForTesting
+ static void reset() {
+ sSettingValue = null;
+ sImmersiveStatusFilter = null;
+ sImmersiveNavigationFilter = null;
+ }
+
+ /**
+ * Registers a content observer to listen to updates to the SYSTEM_BAR_VISIBILITY_OVERRIDE flag.
+ */
+ static void registerContentObserver(Context context, Handler handler, FilterListener listener) {
+ context.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE), false,
+ new ContentObserver(handler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ if (reloadFromSetting(context)) {
+ listener.onFilterUpdated();
+ }
+ }
+ }, UserHandle.USER_ALL);
+ }
+
+ /**
+ * Returns bar visibilities based on POLICY_CONTROL_AUTO filters and window policies.
+ * @return int[], where the first value is the inset types that should be shown, and the second
+ * is the inset types that should be hidden.
+ */
+ @WindowInsets.Type.InsetsType
+ static int[] getBarVisibilities(String packageName) {
+ int hideTypes = 0;
+ int showTypes = 0;
+ if (matchesStatusFilter(packageName)) {
+ hideTypes |= WindowInsets.Type.statusBars();
+ } else {
+ showTypes |= WindowInsets.Type.statusBars();
+ }
+ if (matchesNavigationFilter(packageName)) {
+ hideTypes |= WindowInsets.Type.navigationBars();
+ } else {
+ showTypes |= WindowInsets.Type.navigationBars();
+ }
+
+ return new int[] {showTypes, hideTypes};
+ }
+
+ private static boolean matchesStatusFilter(String packageName) {
+ return sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(packageName);
+ }
+
+ private static boolean matchesNavigationFilter(String packageName) {
+ return sImmersiveNavigationFilter != null
+ && sImmersiveNavigationFilter.matches(packageName);
+ }
+
+ private static void setFilters(String value) {
+ if (DEBUG) Slog.d(TAG, "setFilters: " + value);
+ sImmersiveStatusFilter = null;
+ sImmersiveNavigationFilter = null;
+ if (value != null) {
+ String[] nvps = value.split(":");
+ for (String nvp : nvps) {
+ int i = nvp.indexOf('=');
+ if (i == -1) continue;
+ String n = nvp.substring(0, i);
+ String v = nvp.substring(i + 1);
+ if (n.equals(NAME_IMMERSIVE_FULL)) {
+ Filter f = Filter.parse(v);
+ sImmersiveStatusFilter = sImmersiveNavigationFilter = f;
+ } else if (n.equals(NAME_IMMERSIVE_STATUS)) {
+ Filter f = Filter.parse(v);
+ sImmersiveStatusFilter = f;
+ } else if (n.equals(NAME_IMMERSIVE_NAVIGATION)) {
+ Filter f = Filter.parse(v);
+ sImmersiveNavigationFilter = f;
+ }
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "immersiveStatusFilter: " + sImmersiveStatusFilter);
+ Slog.d(TAG, "immersiveNavigationFilter: " + sImmersiveNavigationFilter);
+ }
+ }
+
+ private static class Filter {
+ private static final String ALL = "*";
+
+ private final ArraySet<String> mWhitelist;
+ private final ArraySet<String> mBlacklist;
+
+ private Filter(ArraySet<String> whitelist, ArraySet<String> blacklist) {
+ mWhitelist = whitelist;
+ mBlacklist = blacklist;
+ }
+
+ boolean matches(String packageName) {
+ if (packageName == null) return false;
+ if (onBlacklist(packageName)) return false;
+ return onWhitelist(packageName);
+ }
+
+ private boolean onBlacklist(String packageName) {
+ return mBlacklist.contains(packageName) || mBlacklist.contains(ALL);
+ }
+
+ private boolean onWhitelist(String packageName) {
+ return mWhitelist.contains(ALL) || mWhitelist.contains(packageName);
+ }
+
+ void dump(PrintWriter pw) {
+ pw.print("Filter[");
+ dump("whitelist", mWhitelist, pw); pw.print(',');
+ dump("blacklist", mBlacklist, pw); pw.print(']');
+ }
+
+ private void dump(String name, ArraySet<String> set, PrintWriter pw) {
+ pw.print(name); pw.print("=(");
+ int n = set.size();
+ for (int i = 0; i < n; i++) {
+ if (i > 0) pw.print(',');
+ pw.print(set.valueAt(i));
+ }
+ pw.print(')');
+ }
+
+ @Override
+ public String toString() {
+ StringWriter sw = new StringWriter();
+ dump(new PrintWriter(sw, true));
+ return sw.toString();
+ }
+
+ // value = comma-delimited list of tokens, where token = (package name|*)
+ // e.g. "com.package1", or "com.android.systemui, com.android.keyguard" or "*"
+ static Filter parse(String value) {
+ if (value == null) return null;
+ ArraySet<String> whitelist = new ArraySet<String>();
+ ArraySet<String> blacklist = new ArraySet<String>();
+ for (String token : value.split(",")) {
+ token = token.trim();
+ if (token.startsWith("-") && token.length() > 1) {
+ token = token.substring(1);
+ blacklist.add(token);
+ } else {
+ whitelist.add(token);
+ }
+ }
+ return new Filter(whitelist, blacklist);
+ }
+ }
+
+ /**
+ * Interface to listen for updates to the filter triggered by the content observer listening to
+ * the SYSTEM_BAR_VISIBILITY_OVERRIDE flag.
+ */
+ interface FilterListener {
+
+ /** Callback triggered when the content observer updates the filter. */
+ void onFilterUpdated();
+ }
+
+ private BarControlPolicy() {}
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
new file mode 100644
index 0000000..a831464
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
@@ -0,0 +1,168 @@
+/*
+ * 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.wm;
+
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.IDisplayWindowInsetsController;
+import android.view.InsetsController;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+import android.view.WindowInsets;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.systemui.TransactionPool;
+import com.android.systemui.dagger.qualifiers.Main;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Controller that maps between displays and {@link IDisplayWindowInsetsController} in order to
+ * give system bar control to SystemUI.
+ * {@link R.bool#config_remoteInsetsControllerControlsSystemBars} determines whether this controller
+ * takes control or not.
+ */
+@Singleton
+public class DisplaySystemBarsController extends DisplayImeController {
+
+ private static final String TAG = "DisplaySystemBarsController";
+
+ private SparseArray<PerDisplay> mPerDisplaySparseArray;
+
+ @Inject
+ public DisplaySystemBarsController(
+ SystemWindows syswin,
+ DisplayController displayController,
+ @Main Handler mainHandler,
+ TransactionPool transactionPool) {
+ super(syswin, displayController, mainHandler, transactionPool);
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ PerDisplay pd = new PerDisplay(displayId);
+ try {
+ mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to set insets controller on display " + displayId);
+ }
+ // Lazy loading policy control filters instead of during boot.
+ if (mPerDisplaySparseArray == null) {
+ mPerDisplaySparseArray = new SparseArray<>();
+ BarControlPolicy.reloadFromSetting(mSystemWindows.mContext);
+ BarControlPolicy.registerContentObserver(mSystemWindows.mContext, mHandler, () -> {
+ int size = mPerDisplaySparseArray.size();
+ for (int i = 0; i < size; i++) {
+ mPerDisplaySparseArray.valueAt(i).modifyDisplayWindowInsets();
+ }
+ });
+ }
+ mPerDisplaySparseArray.put(displayId, pd);
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ try {
+ mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
+ }
+ mPerDisplaySparseArray.remove(displayId);
+ }
+
+ @VisibleForTesting
+ class PerDisplay extends IDisplayWindowInsetsController.Stub {
+
+ int mDisplayId;
+ InsetsController mInsetsController;
+ InsetsState mInsetsState = new InsetsState();
+ String mPackageName;
+
+ PerDisplay(int displayId) {
+ mDisplayId = displayId;
+ mInsetsController = new InsetsController(
+ new DisplaySystemBarsInsetsControllerHost(mHandler, this));
+ }
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ if (mInsetsState.equals(insetsState)) {
+ return;
+ }
+ mInsetsState.set(insetsState, true /* copySources */);
+ mInsetsController.onStateChanged(insetsState);
+ if (mPackageName != null) {
+ modifyDisplayWindowInsets();
+ }
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] activeControls) {
+ mInsetsController.onControlsChanged(activeControls);
+ }
+
+ @Override
+ public void hideInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) {
+ mInsetsController.hide(types);
+ }
+
+ @Override
+ public void showInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) {
+ mInsetsController.show(types);
+ }
+
+ @Override
+ public void topFocusedWindowChanged(String packageName) {
+ // If both package names are null or both package names are equal, return.
+ if (mPackageName == packageName
+ || (mPackageName != null && mPackageName.equals(packageName))) {
+ return;
+ }
+ mPackageName = packageName;
+ modifyDisplayWindowInsets();
+ }
+
+ private void modifyDisplayWindowInsets() {
+ if (mPackageName == null) {
+ return;
+ }
+ int[] barVisibilities = BarControlPolicy.getBarVisibilities(mPackageName);
+ updateInsetsState(barVisibilities[0], /* visible= */ true);
+ updateInsetsState(barVisibilities[1], /* visible= */ false);
+ showInsets(barVisibilities[0], /* fromIme= */ false);
+ hideInsets(barVisibilities[1], /* fromIme= */ false);
+ try {
+ mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to update window manager service.");
+ }
+ }
+
+ private void updateInsetsState(@WindowInsets.Type.InsetsType int types, boolean visible) {
+ ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
+ for (int i = internalTypes.size() - 1; i >= 0; i--) {
+ mInsetsState.getSource(internalTypes.valueAt(i)).setVisible(visible);
+ }
+ }
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsInsetsControllerHost.java b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsInsetsControllerHost.java
new file mode 100644
index 0000000..2f8da44
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsInsetsControllerHost.java
@@ -0,0 +1,173 @@
+/*
+ * 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.wm;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IDisplayWindowInsetsController;
+import android.view.InsetsController;
+import android.view.InsetsState;
+import android.view.SurfaceControl;
+import android.view.SyncRtSurfaceTransactionApplier;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
+import android.view.WindowInsetsController;
+import android.view.inputmethod.InputMethodManager;
+
+import java.util.List;
+
+/**
+ * Implements {@link InsetsController.Host} for usage by
+ * {@link DisplaySystemBarsController.PerDisplay} instances in {@link DisplaySystemBarsController}.
+ * @hide
+ */
+public class DisplaySystemBarsInsetsControllerHost implements InsetsController.Host {
+
+ private static final String TAG = DisplaySystemBarsInsetsControllerHost.class.getSimpleName();
+
+ private final Handler mHandler;
+ private final IDisplayWindowInsetsController mController;
+ private final float[] mTmpFloat9 = new float[9];
+
+ public DisplaySystemBarsInsetsControllerHost(
+ Handler handler, IDisplayWindowInsetsController controller) {
+ mHandler = handler;
+ mController = controller;
+ }
+
+ @Override
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ @Override
+ public void notifyInsetsChanged() {
+ // no-op
+ }
+
+ @Override
+ public void dispatchWindowInsetsAnimationPrepare(@NonNull WindowInsetsAnimation animation) {
+ // no-op
+ }
+
+ @Override
+ public WindowInsetsAnimation.Bounds dispatchWindowInsetsAnimationStart(
+ @NonNull WindowInsetsAnimation animation,
+ @NonNull WindowInsetsAnimation.Bounds bounds) {
+ return null;
+ }
+
+ @Override
+ public WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets,
+ @NonNull List<WindowInsetsAnimation> runningAnimations) {
+ return null;
+ }
+
+ @Override
+ public void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation) {
+ // no-op
+ }
+
+ @Override
+ public void applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
+ for (int i = params.length - 1; i >= 0; i--) {
+ SyncRtSurfaceTransactionApplier.applyParams(
+ new SurfaceControl.Transaction(), params[i], mTmpFloat9);
+ }
+
+ }
+
+ @Override
+ public void updateCompatSysUiVisibility(
+ @InsetsState.InternalInsetsType int type, boolean visible, boolean hasControl) {
+ // no-op
+ }
+
+ @Override
+ public void onInsetsModified(InsetsState insetsState) {
+ try {
+ mController.insetsChanged(insetsState);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send insets to controller");
+ }
+ }
+
+ @Override
+ public boolean hasAnimationCallbacks() {
+ return false;
+ }
+
+ @Override
+ public void setSystemBarsAppearance(
+ @WindowInsetsController.Appearance int appearance,
+ @WindowInsetsController.Appearance int mask) {
+ // no-op
+ }
+
+ @Override
+ public @WindowInsetsController.Appearance int getSystemBarsAppearance() {
+ return 0;
+ }
+
+ @Override
+ public void setSystemBarsBehavior(@WindowInsetsController.Behavior int behavior) {
+ // no-op
+ }
+
+ @Override
+ public @WindowInsetsController.Behavior int getSystemBarsBehavior() {
+ return 0;
+ }
+
+ @Override
+ public void releaseSurfaceControlFromRt(SurfaceControl surfaceControl) {
+ surfaceControl.release();
+ }
+
+ @Override
+ public void addOnPreDrawRunnable(Runnable r) {
+ mHandler.post(r);
+ }
+
+ @Override
+ public void postInsetsAnimationCallback(Runnable r) {
+ mHandler.post(r);
+ }
+
+ @Override
+ public InputMethodManager getInputMethodManager() {
+ return null;
+ }
+
+ @Override
+ public String getRootViewTitle() {
+ return null;
+ }
+
+ @Override
+ public int dipToPx(int dips) {
+ return 0;
+ }
+
+ @Override
+ public IBinder getWindowToken() {
+ return null;
+ }
+}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
index eca51e3..232df2e 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
@@ -24,6 +24,8 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadsetClient;
import android.content.Intent;
import android.os.Handler;
@@ -44,14 +46,19 @@
public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase {
private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
+ private static final String BLUETOOTH_REMOTE_ADDRESS = "00:11:22:33:44:55";
private ConnectedDeviceVoiceRecognitionNotifier mVoiceRecognitionNotifier;
+ private TestableLooper mTestableLooper;
private Handler mTestHandler;
+ private BluetoothDevice mBluetoothDevice;
@Before
public void setUp() throws Exception {
- TestableLooper testableLooper = TestableLooper.get(this);
- mTestHandler = spy(new Handler(testableLooper.getLooper()));
+ mTestableLooper = TestableLooper.get(this);
+ mTestHandler = spy(new Handler(mTestableLooper.getLooper()));
+ mBluetoothDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
+ BLUETOOTH_REMOTE_ADDRESS);
mVoiceRecognitionNotifier = new ConnectedDeviceVoiceRecognitionNotifier(
mContext, mTestHandler);
mVoiceRecognitionNotifier.onBootCompleted();
@@ -61,8 +68,10 @@
public void testReceiveIntent_started_showToast() {
Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
intent.putExtra(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, VOICE_RECOGNITION_STARTED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- waitForIdleSync();
+ mTestableLooper.processAllMessages();
verify(mTestHandler).post(any());
}
@@ -71,8 +80,10 @@
public void testReceiveIntent_invalidExtra_noToast() {
Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
intent.putExtra(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, INVALID_VALUE);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- waitForIdleSync();
+ mTestableLooper.processAllMessages();
verify(mTestHandler, never()).post(any());
}
@@ -80,8 +91,10 @@
@Test
public void testReceiveIntent_noExtra_noToast() {
Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- waitForIdleSync();
+ mTestableLooper.processAllMessages();
verify(mTestHandler, never()).post(any());
}
@@ -89,8 +102,10 @@
@Test
public void testReceiveIntent_invalidIntent_noToast() {
Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- waitForIdleSync();
+ mTestableLooper.processAllMessages();
verify(mTestHandler, never()).post(any());
}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/wm/BarControlPolicyTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/wm/BarControlPolicyTest.java
new file mode 100644
index 0000000..da7cb8e
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/wm/BarControlPolicyTest.java
@@ -0,0 +1,195 @@
+/*
+ * 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.wm;
+
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.car.settings.CarSettings;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class BarControlPolicyTest extends SysuiTestCase {
+
+ private static final String PACKAGE_NAME = "sample.app";
+
+ @Before
+ public void setUp() {
+ BarControlPolicy.reset();
+ }
+
+ @After
+ public void tearDown() {
+ Settings.Global.clearProviderForTest();
+ }
+
+ @Test
+ public void reloadFromSetting_notSet_doesNotSetFilters() {
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ assertThat(BarControlPolicy.sImmersiveStatusFilter).isNull();
+ }
+
+ @Test
+ public void reloadFromSetting_invalidPolicyControlString_doesNotSetFilters() {
+ String text = "sample text";
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ text
+ );
+
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ assertThat(BarControlPolicy.sImmersiveStatusFilter).isNull();
+ }
+
+ @Test
+ public void reloadFromSetting_validPolicyControlString_setsFilters() {
+ String text = "immersive.status=" + PACKAGE_NAME;
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ text
+ );
+
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ assertThat(BarControlPolicy.sImmersiveStatusFilter).isNotNull();
+ }
+
+ @Test
+ public void reloadFromSetting_filtersSet_doesNotSetFiltersAgain() {
+ String text = "immersive.status=" + PACKAGE_NAME;
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ text
+ );
+
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ assertThat(BarControlPolicy.reloadFromSetting(mContext)).isFalse();
+ }
+
+ @Test
+ public void getBarVisibilities_policyControlNotSet_showsSystemBars() {
+ int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
+
+ assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
+ assertThat(visibilities[1]).isEqualTo(0);
+ }
+
+ @Test
+ public void getBarVisibilities_immersiveStatusForAppAndMatchingApp_hidesStatusBar() {
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ "immersive.status=" + PACKAGE_NAME);
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
+
+ assertThat(visibilities[0]).isEqualTo(navigationBars());
+ assertThat(visibilities[1]).isEqualTo(statusBars());
+ }
+
+ @Test
+ public void getBarVisibilities_immersiveStatusForAppAndNonMatchingApp_showsSystemBars() {
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ "immersive.status=" + PACKAGE_NAME);
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ int[] visibilities = BarControlPolicy.getBarVisibilities("sample2.app");
+
+ assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
+ assertThat(visibilities[1]).isEqualTo(0);
+ }
+
+ @Test
+ public void getBarVisibilities_immersiveStatusForAppsAndNonApp_showsSystemBars() {
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ "immersive.status=apps");
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
+
+ assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
+ assertThat(visibilities[1]).isEqualTo(0);
+ }
+
+ @Test
+ public void getBarVisibilities_immersiveFullForAppAndMatchingApp_hidesSystemBars() {
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ "immersive.full=" + PACKAGE_NAME);
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
+
+ assertThat(visibilities[0]).isEqualTo(0);
+ assertThat(visibilities[1]).isEqualTo(statusBars() | navigationBars());
+ }
+
+ @Test
+ public void getBarVisibilities_immersiveFullForAppAndNonMatchingApp_showsSystemBars() {
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ "immersive.full=" + PACKAGE_NAME);
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ int[] visibilities = BarControlPolicy.getBarVisibilities("sample2.app");
+
+ assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
+ assertThat(visibilities[1]).isEqualTo(0);
+ }
+
+ @Test
+ public void getBarVisibilities_immersiveFullForAppsAndNonApp_showsSystemBars() {
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ "immersive.full=apps");
+ BarControlPolicy.reloadFromSetting(mContext);
+
+ int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
+
+ assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
+ assertThat(visibilities[1]).isEqualTo(0);
+ }
+}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
new file mode 100644
index 0000000..29cc8ee
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.wm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+
+import android.car.settings.CarSettings;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.IWindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.TransactionPool;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class DisplaySystemBarsControllerTest extends SysuiTestCase {
+
+ private DisplaySystemBarsController mController;
+
+ private static final int DISPLAY_ID = 1;
+
+ @Mock
+ private SystemWindows mSystemWindows;
+ @Mock
+ private IWindowManager mIWindowManager;
+ @Mock
+ private DisplayController mDisplayController;
+ @Mock
+ private Handler mHandler;
+ @Mock
+ private TransactionPool mTransactionPool;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mSystemWindows.mContext = mContext;
+ mSystemWindows.mWmService = mIWindowManager;
+
+ mController = new DisplaySystemBarsController(
+ mSystemWindows,
+ mDisplayController,
+ mHandler,
+ mTransactionPool
+ );
+ }
+
+ @Test
+ public void onDisplayAdded_setsDisplayWindowInsetsControllerOnWMService()
+ throws RemoteException {
+ mController.onDisplayAdded(DISPLAY_ID);
+
+ verify(mIWindowManager).setDisplayWindowInsetsController(
+ eq(DISPLAY_ID), any(DisplaySystemBarsController.PerDisplay.class));
+ }
+
+ @Test
+ public void onDisplayAdded_loadsBarControlPolicyFilters() {
+ String text = "sample text";
+ Settings.Global.putString(
+ mContext.getContentResolver(),
+ CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
+ text
+ );
+
+ mController.onDisplayAdded(DISPLAY_ID);
+
+ assertThat(BarControlPolicy.sSettingValue).isEqualTo(text);
+ }
+
+ @Test
+ public void onDisplayRemoved_unsetsDisplayWindowInsetsControllerInWMService()
+ throws RemoteException {
+ mController.onDisplayAdded(DISPLAY_ID);
+
+ mController.onDisplayRemoved(DISPLAY_ID);
+
+ verify(mIWindowManager).setDisplayWindowInsetsController(
+ DISPLAY_ID, /* displayWindowInsetsController= */ null);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
index 5a51ac2..973ab89 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
@@ -239,8 +239,8 @@
builder.addAction((new Notification.Action.Builder(
Icon.createWithResource(context, R.drawable.ic_settings_multiuser),
context.getString(R.string.manage_users),
- PendingIntent.getActivity(context, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT))).build());
+ PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT
+ | PendingIntent.FLAG_IMMUTABLE))).build());
}
/**
@@ -259,7 +259,7 @@
builder.addAction((new Notification.Action.Builder(
Icon.createWithResource(context, R.drawable.ic_lock),
context.getString(R.string.manage_device_administrators),
- PendingIntent.getActivity(context, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT))).build());
+ PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT
+ | PendingIntent.FLAG_IMMUTABLE))).build());
}
}
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java
index 9ae3198..5a756fe 100644
--- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java
@@ -46,7 +46,7 @@
private static final String LOG_TAG = "PrintServiceRecService";
/** All registered plugins */
- private ArrayList<RemotePrintServicePlugin> mPlugins;
+ private final ArrayList<RemotePrintServicePlugin> mPlugins = new ArrayList<>();
/** Lock to keep multi-cast enabled */
private WifiManager.MulticastLock mMultiCastLock;
@@ -62,8 +62,6 @@
mMultiCastLock.acquire();
}
- mPlugins = new ArrayList<>();
-
try {
for (VendorConfig config : VendorConfig.getAllConfigs(this)) {
try {
@@ -138,6 +136,7 @@
Log.e(LOG_TAG, "Could not stop plugin", e);
}
}
+ mPlugins.clear();
if (mMultiCastLock != null) {
mMultiCastLock.release();
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
index abdfad5..88b181f 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
@@ -276,21 +276,23 @@
intent.putExtra(EXTRA_PRINT_JOB_ID, printJobId.flattenToString());
intent.setData(Uri.fromParts("printjob", printJobId.flattenToString(), null));
}
- return PendingIntent.getActivity(mContext, 0, intent, 0);
+ return PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);
}
private PendingIntent createCancelIntent(PrintJobInfo printJob) {
Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class);
intent.setAction(INTENT_ACTION_CANCEL_PRINTJOB + "_" + printJob.getId().flattenToString());
intent.putExtra(EXTRA_PRINT_JOB_ID, printJob.getId());
- return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+ return PendingIntent.getBroadcast(mContext, 0, intent,
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
}
private PendingIntent createRestartIntent(PrintJobId printJobId) {
Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class);
intent.setAction(INTENT_ACTION_RESTART_PRINTJOB + "_" + printJobId.flattenToString());
intent.putExtra(EXTRA_PRINT_JOB_ID, printJobId);
- return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+ return PendingIntent.getBroadcast(mContext, 0, intent,
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
}
private static boolean shouldNotifyForState(int state) {
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
index 3565b0e..e829974 100644
--- a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
@@ -16,9 +16,6 @@
package com.android.settingslib.widget;
-import static com.android.settingslib.widget.AdaptiveOutlineDrawable.AdaptiveOutlineIconType.TYPE_ADVANCED;
-import static com.android.settingslib.widget.AdaptiveOutlineDrawable.AdaptiveOutlineIconType.TYPE_DEFAULT;
-
import android.annotation.ColorInt;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -48,11 +45,12 @@
private static final float ADVANCED_ICON_CENTER = 50f;
private static final float ADVANCED_ICON_RADIUS = 48f;
+ public static final int ICON_TYPE_DEFAULT = 0;
+ public static final int ICON_TYPE_ADVANCED = 1;
+
@Retention(RetentionPolicy.SOURCE)
- @IntDef({TYPE_DEFAULT, TYPE_ADVANCED})
+ @IntDef({ICON_TYPE_DEFAULT, ICON_TYPE_ADVANCED})
public @interface AdaptiveOutlineIconType {
- int TYPE_DEFAULT = 0;
- int TYPE_ADVANCED = 1;
}
@VisibleForTesting
@@ -66,7 +64,7 @@
public AdaptiveOutlineDrawable(Resources resources, Bitmap bitmap) {
super(new AdaptiveIconShapeDrawable(resources));
- init(resources, bitmap, TYPE_DEFAULT);
+ init(resources, bitmap, ICON_TYPE_DEFAULT);
}
public AdaptiveOutlineDrawable(Resources resources, Bitmap bitmap,
@@ -96,10 +94,10 @@
private @ColorInt int getColor(Resources resources, @AdaptiveOutlineIconType int type) {
int resId;
switch (type) {
- case TYPE_ADVANCED:
+ case ICON_TYPE_ADVANCED:
resId = R.color.advanced_outline_color;
break;
- case TYPE_DEFAULT:
+ case ICON_TYPE_DEFAULT:
default:
resId = R.color.bt_outline_color;
break;
@@ -110,10 +108,10 @@
private int getDimensionPixelSize(Resources resources, @AdaptiveOutlineIconType int type) {
int resId;
switch (type) {
- case TYPE_ADVANCED:
+ case ICON_TYPE_ADVANCED:
resId = R.dimen.advanced_dashboard_tile_foreground_image_inset;
break;
- case TYPE_DEFAULT:
+ case ICON_TYPE_DEFAULT:
default:
resId = R.dimen.dashboard_tile_foreground_image_inset;
break;
@@ -133,7 +131,7 @@
final int count = canvas.save();
canvas.scale(scaleX, scaleY);
// Draw outline
- if (mType == TYPE_DEFAULT) {
+ if (mType == ICON_TYPE_DEFAULT) {
canvas.drawPath(mPath, mOutlinePaint);
} else {
canvas.drawCircle(ADVANCED_ICON_CENTER, ADVANCED_ICON_CENTER, ADVANCED_ICON_RADIUS,
diff --git a/packages/SettingsLib/Utils/AndroidManifest.xml b/packages/SettingsLib/Utils/AndroidManifest.xml
index fd89676..96d9e51 100644
--- a/packages/SettingsLib/Utils/AndroidManifest.xml
+++ b/packages/SettingsLib/Utils/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.utils">
+ package="com.android.settingslib.widget">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java
index 6125b85..e7c0d96 100644
--- a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java
+++ b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java
@@ -22,7 +22,7 @@
import android.os.UserManager;
import android.util.Log;
-import com.android.settingslib.utils.R;
+import com.android.settingslib.widget.R;
public class AppUtils {
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/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index c27973f..287a1ac 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -22,7 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"No se pueden buscar las redes."</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Ninguna"</string>
- <string name="wifi_remembered" msgid="3266709779723179188">"Guardada"</string>
+ <string name="wifi_remembered" msgid="3266709779723179188">"Guardado"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Desconectado"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Inhabilitada"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Error de configuración IP"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 3e8b1c1..f9d57c4 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -37,7 +37,7 @@
<string name="wifi_no_internet" msgid="1774198889176926299">"Sen acceso a Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Gardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectouse automaticamente a través de %1$s"</string>
- <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectada automaticamente a través dun provedor de valoración de rede"</string>
+ <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectada automaticamente a través dun provedor de valoración de redes"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado a través de %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Wifi conectada a través de <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Dispoñible a través de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 08cf5f9..8407db6 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -211,8 +211,8 @@
<string name="adb_wireless_error" msgid="721958772149779856">"Алдаа"</string>
<string name="adb_wireless_settings" msgid="2295017847215680229">"Wireless debugging"</string>
<string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Боломжтой төхөөрөмжүүдийг харах болох ашиглахын тулд wireless debugging-г асаана уу"</string>
- <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Хурдан хариу үйлдлийн кодоор төхөөрөмжийг хослуул"</string>
- <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Хурдан хариу үйлдлийн кодын сканнер ашиглан шинэ төхөөрөмжүүдийг хослуулна уу"</string>
+ <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"QR кодоор төхөөрөмжийг хослуул"</string>
+ <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"QR кодын сканнер ашиглан шинэ төхөөрөмжүүдийг хослуулна уу"</string>
<string name="adb_pair_method_code_title" msgid="1122590300445142904">"Хослуулах кодоор төхөөрөмжийг хослуулна уу"</string>
<string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Зургаан оронтой кодыг ашиглан шинэ төхөөрөмжүүдийг хослуулна уу"</string>
<string name="adb_paired_devices_title" msgid="5268997341526217362">"Хослуулсан төхөөрөмжүүд"</string>
@@ -226,12 +226,12 @@
<string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Wi‑Fi хослуулах код"</string>
<string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Хослуулалт амжилтгүй боллоо"</string>
<string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Төхөөрөмжийг ижил сүлжээнд холбосон эсэхийг шалгана уу."</string>
- <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Хурдан хариу үйлдлийн кодыг скан хийж Wi-Fi-р төхөөрөмжийг хослуулна уу"</string>
+ <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR кодыг скан хийж Wi-Fi-р төхөөрөмжийг хослуулна уу"</string>
<string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Төхөөрөмжийг хослуулж байна…"</string>
- <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Төхөөрөмжийг хослуулж чадсангүй. Хурдан хариу үйлдлийн код буруу эсвэл төхөөрөмжийг ижил сүлжээнд холбоогүй байна."</string>
+ <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Төхөөрөмжийг хослуулж чадсангүй. QR код буруу эсвэл төхөөрөмжийг ижил сүлжээнд холбоогүй байна."</string>
<string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP хаяг ба порт"</string>
- <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Хурдан хариу үйлдлийн кодыг скан хийх"</string>
- <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Хурдан хариу үйлдлийн кодыг скан хийж Wi-Fi-р төхөөрөмжийг хослуулна уу"</string>
+ <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR кодыг скан хийх"</string>
+ <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR кодыг скан хийж Wi-Fi-р төхөөрөмжийг хослуулна уу"</string>
<string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Wi-Fi сүлжээнд холбогдоно уу"</string>
<string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, дебаг хийх, dev"</string>
<string name="bugreport_in_power" msgid="8664089072534638709">"Алдаа мэдээлэх товчлол"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index e33df4a..32cc39e 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -37,7 +37,7 @@
<string name="wifi_no_internet" msgid="1774198889176926299">"Geen internettoegang"</string>
<string name="saved_network" msgid="7143698034077223645">"Opgeslagen door \'<xliff:g id="NAME">%1$s</xliff:g>\'"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisch verbonden via %1$s"</string>
- <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch verbonden via netwerkbeoordelaar"</string>
+ <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch verbonden via provider van netwerkbeoordelingen"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Verbonden via %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Verbonden via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Beschikbaar via %1$s"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 95e916b..68f7289 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -1,6 +1,6 @@
package com.android.settingslib.bluetooth;
-import static com.android.settingslib.widget.AdaptiveOutlineDrawable.AdaptiveOutlineIconType.TYPE_ADVANCED;
+import static com.android.settingslib.widget.AdaptiveOutlineDrawable.ICON_TYPE_ADVANCED;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -238,7 +238,7 @@
final Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, iconSize,
iconSize, false);
bitmap.recycle();
- return new AdaptiveOutlineDrawable(resources, resizedBitmap, TYPE_ADVANCED);
+ return new AdaptiveOutlineDrawable(resources, resizedBitmap, ICON_TYPE_ADVANCED);
}
return drawable;
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/drawable/CircleFramedDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
index 278b57d..e5ea446 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
@@ -41,7 +41,7 @@
private final Bitmap mBitmap;
private final int mSize;
- private final Paint mPaint;
+ private Paint mIconPaint;
private float mScale;
private Rect mSrcRect;
@@ -75,18 +75,18 @@
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
// opaque circle matte
- mPaint = new Paint();
- mPaint.setAntiAlias(true);
- mPaint.setColor(Color.BLACK);
- mPaint.setStyle(Paint.Style.FILL);
- canvas.drawPath(fillPath, mPaint);
+ Paint paint = new Paint();
+ paint.setAntiAlias(true);
+ paint.setColor(Color.BLACK);
+ paint.setStyle(Paint.Style.FILL);
+ canvas.drawPath(fillPath, paint);
// mask in the icon where the bitmap is opaque
- mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
- canvas.drawBitmap(icon, cropRect, circleRect, mPaint);
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+ canvas.drawBitmap(icon, cropRect, circleRect, paint);
// prepare paint for frame drawing
- mPaint.setXfermode(null);
+ paint.setXfermode(null);
mScale = 1f;
@@ -100,7 +100,7 @@
final float pad = (mSize - inside) / 2f;
mDstRect.set(pad, pad, mSize - pad, mSize - pad);
- canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, null);
+ canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, mIconPaint);
}
public void setScale(float scale) {
@@ -122,8 +122,12 @@
@Override
public void setColorFilter(ColorFilter cf) {
+ if (mIconPaint == null) {
+ mIconPaint = new Paint();
+ }
+ mIconPaint.setColorFilter(cf);
}
-
+
@Override
public int getIntrinsicWidth() {
return mSize;
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/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/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 2da84c2..482a381 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -172,5 +172,6 @@
Settings.Secure.ONE_HANDED_MODE_ENABLED,
Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
Settings.Secure.TAPS_APP_TO_EXIT,
+ Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 3fd543d..b337e60 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -259,5 +259,6 @@
VALIDATORS.put(Secure.ONE_HANDED_MODE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ONE_HANDED_MODE_TIMEOUT, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.TAPS_APP_TO_EXIT, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a946888..a994fda 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2573,6 +2573,10 @@
Settings.Secure.TAPS_APP_TO_EXIT,
SecureSettingsProto.OneHanded.TAPS_APP_TO_EXIT);
+ dumpSetting(s, p,
+ Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
+ SecureSettingsProto.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED);
+
// Please insert new settings using the same order as in SecureSettingsProto.
p.end(token);
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 7a27676..6ecf303 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -68,14 +68,14 @@
"iconloader_base",
"SystemUI-tags",
"SystemUI-proto",
- "dagger2-2.19",
+ "dagger2",
"jsr330"
],
manifest: "AndroidManifest.xml",
kotlincflags: ["-Xjvm-default=enable"],
- plugins: ["dagger2-compiler-2.19"],
+ plugins: ["dagger2-compiler"],
}
filegroup {
@@ -139,7 +139,7 @@
"mockito-target-extended-minus-junit4",
"testables",
"truth-prebuilt",
- "dagger2-2.19",
+ "dagger2",
"jsr330"
],
libs: [
@@ -151,7 +151,7 @@
"--extra-packages",
"com.android.systemui",
],
- plugins: ["dagger2-compiler-2.19"],
+ plugins: ["dagger2-compiler"],
}
android_app {
diff --git a/packages/SystemUI/docs/dagger.md b/packages/SystemUI/docs/dagger.md
index c440fba..bb68647 100644
--- a/packages/SystemUI/docs/dagger.md
+++ b/packages/SystemUI/docs/dagger.md
@@ -206,11 +206,31 @@
## Updating Dagger2
+We depend on the Dagger source found in external/dagger2. We should automatically pick up on updates
+when that repository is updated.
+
+*Deprecated:*
+
Binaries can be downloaded from https://repo1.maven.org/maven2/com/google/dagger/ and then loaded
into
[/prebuilts/tools/common/m2/repository/com/google/dagger/](http://cs/android/prebuilts/tools/common/m2/repository/com/google/dagger/)
+The following commands should work, substituting in the version that you are looking for:
+````
+cd prebuilts/tools/common/m2/repository/com/google/dagger/
+
+wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger/2.28.1/
+
+wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-compiler/2.28.1/
+
+wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-spi/2.28.1/
+
+wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-producers/2.28.1/
+````
+
+Then update `prebuilts/tools/common/m2/Android.bp` to point at your new jars.
+
## TODO List
- Eliminate usages of Dependency#get
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 597644b..4527c6c 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -53,6 +53,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp"
+ android:importantForAccessibility="no"
android:layout_weight="1">
<com.android.systemui.qs.QSPanel
android:id="@+id/quick_settings_panel"
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
index f474a36..732758a 100644
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ b/packages/SystemUI/res/layout/udfps_view.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<com.google.android.systemui.udfps.UdfpsView
+<com.android.systemui.biometrics.UdfpsView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/udfps_view"
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/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
index ee2d001..9d3620f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
@@ -25,6 +25,7 @@
import android.app.ActivityManager.TaskSnapshot;
import android.graphics.Bitmap;
import android.graphics.Color;
+import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.util.Log;
@@ -62,10 +63,12 @@
public ThumbnailData(TaskSnapshot snapshot) {
final HardwareBuffer buffer = snapshot.getHardwareBuffer();
- if (buffer != null && (buffer.getUsage() & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) {
+ if (buffer == null || (buffer.getUsage() & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) {
// TODO(b/157562905): Workaround for a crash when we get a snapshot without this state
- Log.e("ThumbnailData", "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE");
- thumbnail = Bitmap.createBitmap(buffer.getWidth(), buffer.getHeight(), ARGB_8888);
+ Log.e("ThumbnailData", "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE: "
+ + buffer);
+ Point taskSize = snapshot.getTaskSize();
+ thumbnail = Bitmap.createBitmap(taskSize.x, taskSize.y, ARGB_8888);
thumbnail.eraseColor(Color.BLACK);
} else {
thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.getColorSpace());
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
index b966f93..255fffd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
@@ -114,6 +114,11 @@
t.deferTransactionUntil(surfaceControl, barrier, frameNumber);
}
+ public static void setRelativeLayer(Transaction t, SurfaceControl surfaceControl,
+ SurfaceControl relativeTo, int z) {
+ t.setRelativeLayer(surfaceControl, relativeTo, z);
+ }
+
@Deprecated
public static void setEarlyWakeup(Transaction t) {
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 10dcbd6..a84664c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -448,8 +448,8 @@
if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
+ " UpdateSim.onSimCheckResponse: "
+ " attemptsRemaining=" + result.getAttemptsRemaining());
- mStateMachine.reset();
}
+ mStateMachine.reset();
mCheckSimPukThread = null;
}
});
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/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 6c06553..ad11d71 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -62,6 +62,7 @@
import android.provider.Settings.Secure;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayCutout.BoundsPosition;
import android.view.DisplayInfo;
@@ -820,6 +821,7 @@
private static final float HIDDEN_CAMERA_PROTECTION_SCALE = 0.5f;
+ private Display.Mode mDisplayMode = null;
private final DisplayInfo mInfo = new DisplayInfo();
private final Paint mPaint = new Paint();
private final List<Rect> mBounds = new ArrayList();
@@ -904,11 +906,33 @@
@Override
public void onDisplayChanged(int displayId) {
+ Display.Mode oldMode = mDisplayMode;
+ mDisplayMode = getDisplay().getMode();
+
+ // Display mode hasn't meaningfully changed, we can ignore it
+ if (!modeChanged(oldMode, mDisplayMode)) {
+ return;
+ }
+
if (displayId == getDisplay().getDisplayId()) {
update();
}
}
+ private boolean modeChanged(Display.Mode oldMode, Display.Mode newMode) {
+ if (oldMode == null) {
+ return true;
+ }
+
+ boolean changed = false;
+ changed |= oldMode.getPhysicalHeight() != newMode.getPhysicalHeight();
+ changed |= oldMode.getPhysicalWidth() != newMode.getPhysicalWidth();
+ // We purposely ignore refresh rate and id changes here, because we don't need to
+ // invalidate for those, and they can trigger the refresh rate to increase
+
+ return changed;
+ }
+
public void setRotation(int rotation) {
mRotation = rotation;
update();
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 941de2d..5fd7b53 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -21,9 +21,9 @@
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -57,12 +57,11 @@
private static final long NOTED_OP_TIME_DELAY_MS = 5000;
private static final String TAG = "AppOpsControllerImpl";
private static final boolean DEBUG = false;
- private final Context mContext;
private final AppOpsManager mAppOps;
private H mBGHandler;
private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
- private final ArrayMap<Integer, Set<Callback>> mCallbacksByCode = new ArrayMap<>();
+ private final SparseArray<Set<Callback>> mCallbacksByCode = new SparseArray<>();
private boolean mListening;
@GuardedBy("mActiveItems")
@@ -71,6 +70,7 @@
private final List<AppOpItem> mNotedItems = new ArrayList<>();
protected static final int[] OPS = new int[] {
+ AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
AppOpsManager.OP_CAMERA,
AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
AppOpsManager.OP_RECORD_AUDIO,
@@ -83,7 +83,6 @@
Context context,
@Background Looper bgLooper,
DumpManager dumpManager) {
- mContext = context;
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mBGHandler = new H(bgLooper);
final int numOps = OPS.length;
@@ -131,7 +130,7 @@
boolean added = false;
final int numCodes = opsCodes.length;
for (int i = 0; i < numCodes; i++) {
- if (mCallbacksByCode.containsKey(opsCodes[i])) {
+ if (mCallbacksByCode.contains(opsCodes[i])) {
mCallbacksByCode.get(opsCodes[i]).add(callback);
added = true;
} else {
@@ -155,7 +154,7 @@
public void removeCallback(int[] opsCodes, AppOpsController.Callback callback) {
final int numCodes = opsCodes.length;
for (int i = 0; i < numCodes; i++) {
- if (mCallbacksByCode.containsKey(opsCodes[i])) {
+ if (mCallbacksByCode.contains(opsCodes[i])) {
mCallbacksByCode.get(opsCodes[i]).remove(callback);
}
}
@@ -312,7 +311,7 @@
}
private void notifySuscribers(int code, int uid, String packageName, boolean active) {
- if (mCallbacksByCode.containsKey(code)) {
+ if (mCallbacksByCode.contains(code)) {
if (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName);
for (Callback cb: mCallbacksByCode.get(code)) {
cb.onActiveStateChanged(code, uid, packageName, active);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index c82e1de..97e97ff 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -22,6 +22,8 @@
import android.graphics.Point;
import android.hardware.fingerprint.IFingerprintService;
import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.Handler;
+import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import android.view.LayoutInflater;
@@ -44,6 +46,7 @@
private final Context mContext;
private final IFingerprintService mFingerprintService;
private final WindowManager mWindowManager;
+ private final Handler mHandler;
private UdfpsView mView;
private WindowManager.LayoutParams mLayoutParams;
@@ -96,6 +99,7 @@
mContext = context;
mFingerprintService = fingerprintService;
mWindowManager = windowManager;
+ mHandler = new Handler(Looper.getMainLooper());
start();
}
@@ -138,23 +142,27 @@
}
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);
+ mHandler.post(() -> {
+ 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;
- }
+ mHandler.post(() -> {
+ Log.v(TAG, "hideUdfpsOverlay | removing window");
+ if (mIsOverlayShowing) {
+ mWindowManager.removeView(mView);
+ mIsOverlayShowing = false;
+ }
+ });
}
private void onFingerDown(int x, int y, float minor, float major) {
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/bubbles/BubbleIconFactory.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
index 40a93e1..57e2362 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
@@ -24,7 +24,6 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
-import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -95,9 +94,16 @@
Bitmap badgeAndRing = Bitmap.createBitmap(userBadgedBitmap.getWidth(),
userBadgedBitmap.getHeight(), userBadgedBitmap.getConfig());
Canvas c = new Canvas(badgeAndRing);
- Rect dest = new Rect((int) ringStrokeWidth, (int) ringStrokeWidth,
- c.getHeight() - (int) ringStrokeWidth, c.getWidth() - (int) ringStrokeWidth);
- c.drawBitmap(userBadgedBitmap, null, dest, null);
+
+ final int bitmapTop = (int) ringStrokeWidth;
+ final int bitmapLeft = (int) ringStrokeWidth;
+ final int bitmapWidth = c.getWidth() - 2 * (int) ringStrokeWidth;
+ final int bitmapHeight = c.getHeight() - 2 * (int) ringStrokeWidth;
+
+ Bitmap scaledBitmap = Bitmap.createScaledBitmap(userBadgedBitmap, bitmapWidth,
+ bitmapHeight, /* filter */ true);
+ c.drawBitmap(scaledBitmap, bitmapTop, bitmapLeft, /* paint */null);
+
Paint ringPaint = new Paint();
ringPaint.setStyle(Paint.Style.STROKE);
ringPaint.setColor(importantConversationColor);
@@ -105,6 +111,7 @@
ringPaint.setStrokeWidth(ringStrokeWidth);
c.drawCircle(c.getWidth() / 2, c.getHeight() / 2, c.getWidth() / 2 - ringStrokeWidth,
ringPaint);
+
shadowGenerator.recreateIcon(Bitmap.createBitmap(badgeAndRing), c);
return createIconBitmap(badgeAndRing);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 0873328..4bd046e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -24,6 +24,7 @@
import com.android.systemui.BootCompleteCache;
import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.assist.AssistModule;
+import com.android.systemui.doze.dagger.DozeComponent;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.model.SysUiState;
@@ -68,6 +69,7 @@
},
subcomponents = {StatusBarComponent.class,
NotificationRowComponent.class,
+ DozeComponent.class,
ExpandableNotificationRowComponent.class})
public abstract class SystemUIModule {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
index 4fea45c..60ee806 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
@@ -51,6 +51,14 @@
static final String KEY_WALLPAPER_VISIBILITY_MS = "wallpaper_visibility_timeout";
static final String KEY_WALLPAPER_FADE_OUT_MS = "wallpaper_fade_out_duration";
+
+ /**
+ * Integer used to dim the screen while dozing.
+ *
+ * @see R.integer.config_screenBrightnessDoze
+ */
+ public int defaultDozeBrightness;
+
/**
* Integer array to map ambient brightness type to real screen brightness.
*
@@ -165,6 +173,8 @@
DEFAULT_WALLPAPER_FADE_OUT_MS);
wallpaperVisibilityDuration = mParser.getLong(KEY_WALLPAPER_VISIBILITY_MS,
DEFAULT_WALLPAPER_VISIBILITY_MS);
+ defaultDozeBrightness = resources.getInteger(
+ com.android.internal.R.integer.config_screenBrightnessDoze);
screenBrightnessArray = mParser.getIntArray(KEY_SCREEN_BRIGHTNESS_ARRAY,
resources.getIntArray(
R.array.config_doze_brightness_sensor_to_brightness));
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java b/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java
index abd41d4..5eb9808 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java
@@ -16,20 +16,22 @@
package com.android.systemui.doze;
-import android.content.Context;
-
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.Dependency;
+import com.android.systemui.doze.dagger.DozeScope;
+
+import javax.inject.Inject;
/**
* Controls removing Keyguard authorization when the phone goes to sleep.
*/
+@DozeScope
public class DozeAuthRemover implements DozeMachine.Part {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- public DozeAuthRemover(Context context) {
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
+ @Inject
+ public DozeAuthRemover(KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
index 554457b..2a3d67f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
@@ -22,33 +22,41 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.DozeMachine.State;
+import com.android.systemui.doze.dagger.DozeScope;
import java.io.PrintWriter;
+import javax.inject.Inject;
+
/**
* Handles dock events for ambient state changes.
*/
+@DozeScope
public class DozeDockHandler implements DozeMachine.Part {
private static final String TAG = "DozeDockHandler";
private static final boolean DEBUG = DozeService.DEBUG;
private final AmbientDisplayConfiguration mConfig;
- private final DozeMachine mMachine;
+ private DozeMachine mMachine;
private final DockManager mDockManager;
private final DockEventListener mDockEventListener;
private int mDockState = DockManager.STATE_NONE;
- DozeDockHandler(AmbientDisplayConfiguration config, DozeMachine machine,
- DockManager dockManager) {
- mMachine = machine;
+ @Inject
+ DozeDockHandler(AmbientDisplayConfiguration config, DockManager dockManager) {
mConfig = config;
mDockManager = dockManager;
mDockEventListener = new DockEventListener();
}
@Override
+ public void setDozeMachine(DozeMachine dozeMachine) {
+ mMachine = dozeMachine;
+ }
+
+ @Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
switch (newState) {
case INITIALIZED:
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
deleted file mode 100644
index 3bac196..0000000
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2016 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.doze;
-
-import android.annotation.Nullable;
-import android.app.AlarmManager;
-import android.app.IWallpaperManager;
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorManager;
-import android.hardware.display.AmbientDisplayConfiguration;
-import android.os.Handler;
-
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.R;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dock.DockManager;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.statusbar.phone.BiometricUnlockController;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.util.sensors.AsyncSensorManager;
-import com.android.systemui.util.sensors.ProximitySensor;
-import com.android.systemui.util.wakelock.DelayedWakeLock;
-import com.android.systemui.util.wakelock.WakeLock;
-
-import javax.inject.Inject;
-
-public class DozeFactory {
-
- private final FalsingManager mFalsingManager;
- private final DozeLog mDozeLog;
- private final DozeParameters mDozeParameters;
- private final BatteryController mBatteryController;
- private final AsyncSensorManager mAsyncSensorManager;
- private final AlarmManager mAlarmManager;
- private final WakefulnessLifecycle mWakefulnessLifecycle;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final DockManager mDockManager;
- private final IWallpaperManager mWallpaperManager;
- private final ProximitySensor mProximitySensor;
- private final ProximitySensor.ProximityCheck mProximityCheck;
- private final DelayedWakeLock.Builder mDelayedWakeLockBuilder;
- private final Handler mHandler;
- private final BiometricUnlockController mBiometricUnlockController;
- private final BroadcastDispatcher mBroadcastDispatcher;
- private final DozeHost mDozeHost;
-
- @Inject
- public DozeFactory(FalsingManager falsingManager, DozeLog dozeLog,
- DozeParameters dozeParameters, BatteryController batteryController,
- AsyncSensorManager asyncSensorManager, AlarmManager alarmManager,
- WakefulnessLifecycle wakefulnessLifecycle, KeyguardUpdateMonitor keyguardUpdateMonitor,
- DockManager dockManager, @Nullable IWallpaperManager wallpaperManager,
- ProximitySensor proximitySensor, ProximitySensor.ProximityCheck proximityCheck,
- DelayedWakeLock.Builder delayedWakeLockBuilder, @Main Handler handler,
- BiometricUnlockController biometricUnlockController,
- BroadcastDispatcher broadcastDispatcher, DozeHost dozeHost) {
- mFalsingManager = falsingManager;
- mDozeLog = dozeLog;
- mDozeParameters = dozeParameters;
- mBatteryController = batteryController;
- mAsyncSensorManager = asyncSensorManager;
- mAlarmManager = alarmManager;
- mWakefulnessLifecycle = wakefulnessLifecycle;
- mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mDockManager = dockManager;
- mWallpaperManager = wallpaperManager;
- mProximitySensor = proximitySensor;
- mProximityCheck = proximityCheck;
- mDelayedWakeLockBuilder = delayedWakeLockBuilder;
- mHandler = handler;
- mBiometricUnlockController = biometricUnlockController;
- mBroadcastDispatcher = broadcastDispatcher;
- mDozeHost = dozeHost;
- }
-
- /** Creates a DozeMachine with its parts for {@code dozeService}. */
- DozeMachine assembleMachine(DozeService dozeService) {
- AmbientDisplayConfiguration config = new AmbientDisplayConfiguration(dozeService);
- WakeLock wakeLock = mDelayedWakeLockBuilder.setHandler(mHandler).setTag("Doze").build();
-
- DozeMachine.Service wrappedService = dozeService;
- wrappedService = new DozeBrightnessHostForwarder(wrappedService, mDozeHost);
- wrappedService = DozeScreenStatePreventingAdapter.wrapIfNeeded(
- wrappedService, mDozeParameters);
- wrappedService = DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(
- wrappedService, mDozeParameters);
-
- DozeMachine machine = new DozeMachine(wrappedService, config, wakeLock,
- mWakefulnessLifecycle, mBatteryController, mDozeLog, mDockManager,
- mDozeHost);
- machine.setParts(new DozeMachine.Part[]{
- new DozePauser(mHandler, machine, mAlarmManager, mDozeParameters.getPolicy()),
- new DozeFalsingManagerAdapter(mFalsingManager),
- createDozeTriggers(dozeService, mAsyncSensorManager, mDozeHost,
- mAlarmManager, config, mDozeParameters, wakeLock,
- machine, mDockManager, mDozeLog, mProximityCheck),
- createDozeUi(dozeService, mDozeHost, wakeLock, machine, mHandler,
- mAlarmManager, mDozeParameters, mDozeLog),
- new DozeScreenState(wrappedService, mHandler, mDozeHost, mDozeParameters,
- wakeLock),
- createDozeScreenBrightness(dozeService, wrappedService, mAsyncSensorManager,
- mDozeHost, mDozeParameters, mHandler),
- new DozeWallpaperState(mWallpaperManager, mBiometricUnlockController,
- mDozeParameters),
- new DozeDockHandler(config, machine, mDockManager),
- new DozeAuthRemover(dozeService)
- });
-
- return machine;
- }
-
- private DozeMachine.Part createDozeScreenBrightness(Context context,
- DozeMachine.Service service, SensorManager sensorManager, DozeHost host,
- DozeParameters params, Handler handler) {
- Sensor sensor = DozeSensors.findSensorWithType(sensorManager,
- context.getString(R.string.doze_brightness_sensor_type));
- return new DozeScreenBrightness(context, service, sensorManager, sensor,
- mBroadcastDispatcher, host, handler, params.getPolicy());
- }
-
- private DozeTriggers createDozeTriggers(Context context, AsyncSensorManager sensorManager,
- DozeHost host, AlarmManager alarmManager, AmbientDisplayConfiguration config,
- DozeParameters params, WakeLock wakeLock,
- DozeMachine machine, DockManager dockManager, DozeLog dozeLog,
- ProximitySensor.ProximityCheck proximityCheck) {
- boolean allowPulseTriggers = true;
- return new DozeTriggers(context, machine, host, alarmManager, config, params,
- sensorManager, wakeLock, allowPulseTriggers, dockManager,
- mProximitySensor, proximityCheck, dozeLog, mBroadcastDispatcher);
-
- }
-
- private DozeMachine.Part createDozeUi(Context context, DozeHost host, WakeLock wakeLock,
- DozeMachine machine, Handler handler, AlarmManager alarmManager,
- DozeParameters params, DozeLog dozeLog) {
- return new DozeUi(context, alarmManager, machine, wakeLock, host, handler, params,
- mKeyguardUpdateMonitor, dozeLog);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java
index 250308d..94b8ba3 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java
@@ -16,15 +16,20 @@
package com.android.systemui.doze;
+import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.plugins.FalsingManager;
+import javax.inject.Inject;
+
/**
* Notifies FalsingManager of whether or not AOD is showing.
*/
+@DozeScope
public class DozeFalsingManagerAdapter implements DozeMachine.Part {
private final FalsingManager mFalsingManager;
+ @Inject
public DozeFalsingManagerAdapter(FalsingManager falsingManager) {
mFalsingManager = falsingManager;
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index ae7d82a..b9d23ad 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -28,6 +28,8 @@
import com.android.internal.util.Preconditions;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.doze.dagger.DozeScope;
+import com.android.systemui.doze.dagger.WrappedService;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -38,6 +40,8 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import javax.inject.Inject;
+
/**
* Orchestrates all things doze.
*
@@ -46,6 +50,7 @@
*
* During state transitions and in certain states, DozeMachine holds a wake lock.
*/
+@DozeScope
public class DozeMachine {
static final String TAG = "DozeMachine";
@@ -146,9 +151,11 @@
private boolean mWakeLockHeldForCurrentState = false;
private DockManager mDockManager;
- public DozeMachine(Service service, AmbientDisplayConfiguration config, WakeLock wakeLock,
- WakefulnessLifecycle wakefulnessLifecycle, BatteryController batteryController,
- DozeLog dozeLog, DockManager dockManager, DozeHost dozeHost) {
+ @Inject
+ public DozeMachine(@WrappedService Service service, AmbientDisplayConfiguration config,
+ WakeLock wakeLock, WakefulnessLifecycle wakefulnessLifecycle,
+ BatteryController batteryController, DozeLog dozeLog, DockManager dockManager,
+ DozeHost dozeHost, Part[] parts) {
mDozeService = service;
mConfig = config;
mWakefulnessLifecycle = wakefulnessLifecycle;
@@ -157,6 +164,10 @@
mDozeLog = dozeLog;
mDockManager = dockManager;
mDozeHost = dozeHost;
+ mParts = parts;
+ for (Part part : parts) {
+ part.setDozeMachine(this);
+ }
}
/**
@@ -168,12 +179,6 @@
}
}
- /** Initializes the set of {@link Part}s. Must be called exactly once after construction. */
- public void setParts(Part[] parts) {
- Preconditions.checkState(mParts == null);
- mParts = parts;
- }
-
/**
* Requests transitioning to {@code requestedState}.
*
@@ -432,6 +437,9 @@
/** Alerts that the screenstate is being changed. */
default void onScreenState(int state) {}
+
+ /** Sets the {@link DozeMachine} when this Part is associated with one. */
+ default void setDozeMachine(DozeMachine dozeMachine) {}
}
/** A wrapper interface for {@link android.service.dreams.DreamService} */
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java
index 58f1448..2b40678 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java
@@ -19,25 +19,35 @@
import android.app.AlarmManager;
import android.os.Handler;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.util.AlarmTimeout;
+import javax.inject.Inject;
+
/**
* Moves the doze machine from the pausing to the paused state after a timeout.
*/
+@DozeScope
public class DozePauser implements DozeMachine.Part {
public static final String TAG = DozePauser.class.getSimpleName();
private final AlarmTimeout mPauseTimeout;
- private final DozeMachine mMachine;
+ private DozeMachine mMachine;
private final AlwaysOnDisplayPolicy mPolicy;
- public DozePauser(Handler handler, DozeMachine machine, AlarmManager alarmManager,
+ @Inject
+ public DozePauser(@Main Handler handler, AlarmManager alarmManager,
AlwaysOnDisplayPolicy policy) {
- mMachine = machine;
mPauseTimeout = new AlarmTimeout(alarmManager, this::onTimeout, TAG, handler);
mPolicy = policy;
}
@Override
+ public void setDozeMachine(DozeMachine dozeMachine) {
+ mMachine = dozeMachine;
+ }
+
+ @Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
switch (newState) {
case DOZE_AOD_PAUSING:
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 64cfb4b..342818d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -19,7 +19,6 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -31,12 +30,17 @@
import android.provider.Settings;
import android.view.Display;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.doze.dagger.BrightnessSensor;
+import com.android.systemui.doze.dagger.DozeScope;
+import com.android.systemui.doze.dagger.WrappedService;
+import com.android.systemui.util.sensors.AsyncSensorManager;
+
+import javax.inject.Inject;
/**
* Controls the screen brightness when dozing.
*/
+@DozeScope
public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachine.Part,
SensorEventListener {
private static final boolean DEBUG_AOD_BRIGHTNESS = SystemProperties
@@ -51,10 +55,8 @@
private final Handler mHandler;
private final SensorManager mSensorManager;
private final Sensor mLightSensor;
- private final BroadcastDispatcher mBroadcastDispatcher;
private final int[] mSensorToBrightness;
private final int[] mSensorToScrimOpacity;
- private final boolean mDebuggable;
private boolean mRegistered;
private int mDefaultDozeBrightness;
@@ -71,40 +73,20 @@
private int mDebugBrightnessBucket = -1;
private DozeMachine.State mState;
- @VisibleForTesting
- public DozeScreenBrightness(Context context, DozeMachine.Service service,
- SensorManager sensorManager, Sensor lightSensor,
- BroadcastDispatcher broadcastDispatcher, DozeHost host,
- Handler handler, int defaultDozeBrightness, int[] sensorToBrightness,
- int[] sensorToScrimOpacity, boolean debuggable) {
+ @Inject
+ public DozeScreenBrightness(Context context, @WrappedService DozeMachine.Service service,
+ AsyncSensorManager sensorManager, @BrightnessSensor Sensor lightSensor,
+ DozeHost host, Handler handler, AlwaysOnDisplayPolicy alwaysOnDisplayPolicy) {
mContext = context;
mDozeService = service;
mSensorManager = sensorManager;
mLightSensor = lightSensor;
- mBroadcastDispatcher = broadcastDispatcher;
mDozeHost = host;
mHandler = handler;
- mDebuggable = debuggable;
- mDefaultDozeBrightness = defaultDozeBrightness;
- mSensorToBrightness = sensorToBrightness;
- mSensorToScrimOpacity = sensorToScrimOpacity;
-
- if (mDebuggable) {
- IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_AOD_BRIGHTNESS);
- mBroadcastDispatcher.registerReceiverWithHandler(this, filter, handler, UserHandle.ALL);
- }
- }
-
- public DozeScreenBrightness(Context context, DozeMachine.Service service,
- SensorManager sensorManager, Sensor lightSensor,
- BroadcastDispatcher broadcastDispatcher, DozeHost host, Handler handler,
- AlwaysOnDisplayPolicy policy) {
- this(context, service, sensorManager, lightSensor, broadcastDispatcher, host, handler,
- context.getResources().getInteger(
- com.android.internal.R.integer.config_screenBrightnessDoze),
- policy.screenBrightnessArray, policy.dimmingScrimArray, DEBUG_AOD_BRIGHTNESS);
+ mDefaultDozeBrightness = alwaysOnDisplayPolicy.defaultDozeBrightness;
+ mSensorToBrightness = alwaysOnDisplayPolicy.screenBrightnessArray;
+ mSensorToScrimOpacity = alwaysOnDisplayPolicy.dimmingScrimArray;
}
@Override
@@ -139,9 +121,6 @@
private void onDestroy() {
setLightSensorEnabled(false);
- if (mDebuggable) {
- mBroadcastDispatcher.unregisterReceiver(this);
- }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 9153593..8c50a16 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -26,13 +26,19 @@
import android.util.Log;
import android.view.Display;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.doze.dagger.DozeScope;
+import com.android.systemui.doze.dagger.WrappedService;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.wakelock.SettableWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
+import javax.inject.Inject;
+
/**
* Controls the screen when dozing.
*/
+@DozeScope
public class DozeScreenState implements DozeMachine.Part {
private static final boolean DEBUG = DozeService.DEBUG;
@@ -59,8 +65,9 @@
private int mPendingScreenState = Display.STATE_UNKNOWN;
private SettableWakeLock mWakeLock;
- public DozeScreenState(DozeMachine.Service service, Handler handler, DozeHost host,
- DozeParameters parameters, WakeLock wakeLock) {
+ @Inject
+ public DozeScreenState(@WrappedService DozeMachine.Service service, @Main Handler handler,
+ DozeHost host, DozeParameters parameters, WakeLock wakeLock) {
mDozeService = service;
mHandler = handler;
mParameters = parameters;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index aebf41b..37bdda8 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -21,7 +21,6 @@
import android.annotation.AnyThread;
import android.app.ActivityManager;
-import android.app.AlarmManager;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
@@ -64,10 +63,8 @@
private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl();
private final Context mContext;
- private final AlarmManager mAlarmManager;
private final AsyncSensorManager mSensorManager;
private final ContentResolver mResolver;
- private final DozeParameters mDozeParameters;
private final AmbientDisplayConfiguration mConfig;
private final WakeLock mWakeLock;
private final Consumer<Boolean> mProxCallback;
@@ -98,14 +95,12 @@
}
}
- public DozeSensors(Context context, AlarmManager alarmManager, AsyncSensorManager sensorManager,
+ DozeSensors(Context context, AsyncSensorManager sensorManager,
DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog,
ProximitySensor proximitySensor) {
mContext = context;
- mAlarmManager = alarmManager;
mSensorManager = sensorManager;
- mDozeParameters = dozeParameters;
mConfig = config;
mWakeLock = wakeLock;
mProxCallback = proxCallback;
@@ -206,7 +201,10 @@
return findSensorWithType(mSensorManager, type);
}
- static Sensor findSensorWithType(SensorManager sensorManager, String type) {
+ /**
+ * Utility method to find a {@link Sensor} for the supplied string type.
+ */
+ public static Sensor findSensorWithType(SensorManager sensorManager, String type) {
if (TextUtils.isEmpty(type)) {
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index d2bebb7..19b0ea1 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -22,6 +22,7 @@
import android.service.dreams.DreamService;
import android.util.Log;
+import com.android.systemui.doze.dagger.DozeComponent;
import com.android.systemui.plugins.DozeServicePlugin;
import com.android.systemui.plugins.DozeServicePlugin.RequestDoze;
import com.android.systemui.plugins.PluginListener;
@@ -36,16 +37,16 @@
implements DozeMachine.Service, RequestDoze, PluginListener<DozeServicePlugin> {
private static final String TAG = "DozeService";
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private final DozeFactory mDozeFactory;
+ private final DozeComponent.Builder mDozeComponentBuilder;
private DozeMachine mDozeMachine;
private DozeServicePlugin mDozePlugin;
private PluginManager mPluginManager;
@Inject
- public DozeService(DozeFactory dozeFactory, PluginManager pluginManager) {
+ public DozeService(DozeComponent.Builder dozeComponentBuilder, PluginManager pluginManager) {
+ mDozeComponentBuilder = dozeComponentBuilder;
setDebug(DEBUG);
- mDozeFactory = dozeFactory;
mPluginManager = pluginManager;
}
@@ -56,7 +57,8 @@
setWindowless(true);
mPluginManager.addPluginListener(this, DozeServicePlugin.class, false /* allowMultiple */);
- mDozeMachine = mDozeFactory.assembleMachine(this);
+ DozeComponent dozeComponent = mDozeComponentBuilder.build(this);
+ mDozeMachine = dozeComponent.getDozeMachine();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index eb2463b..0800a20 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -41,6 +41,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.Assert;
import com.android.systemui.util.sensors.AsyncSensorManager;
@@ -51,9 +52,12 @@
import java.util.Optional;
import java.util.function.Consumer;
+import javax.inject.Inject;
+
/**
* Handles triggers for ambient state changes.
*/
+@DozeScope
public class DozeTriggers implements DozeMachine.Part {
private static final String TAG = "DozeTriggers";
@@ -73,7 +77,7 @@
private static final int PROXIMITY_TIMEOUT_DELAY_MS = 500;
private final Context mContext;
- private final DozeMachine mMachine;
+ private DozeMachine mMachine;
private final DozeLog mDozeLog;
private final DozeSensors mDozeSensors;
private final DozeHost mDozeHost;
@@ -153,21 +157,21 @@
}
}
- public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost,
+ @Inject
+ public DozeTriggers(Context context, DozeHost dozeHost,
AlarmManager alarmManager, AmbientDisplayConfiguration config,
DozeParameters dozeParameters, AsyncSensorManager sensorManager,
- WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager,
+ WakeLock wakeLock, DockManager dockManager,
ProximitySensor proximitySensor, ProximitySensor.ProximityCheck proxCheck,
DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher) {
mContext = context;
- mMachine = machine;
mDozeHost = dozeHost;
mConfig = config;
mDozeParameters = dozeParameters;
mSensorManager = sensorManager;
mWakeLock = wakeLock;
- mAllowPulseTriggers = allowPulseTriggers;
- mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
+ mAllowPulseTriggers = true;
+ mDozeSensors = new DozeSensors(context, mSensorManager, dozeParameters,
config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mDockManager = dockManager;
@@ -177,6 +181,11 @@
}
@Override
+ public void setDozeMachine(DozeMachine dozeMachine) {
+ mMachine = dozeMachine;
+ }
+
+ @Override
public void destroy() {
mDozeSensors.destroy();
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 1c05621..0fdaae8 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -29,15 +29,20 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.AlarmTimeout;
import com.android.systemui.util.wakelock.WakeLock;
import java.util.Calendar;
+import javax.inject.Inject;
+
/**
* The policy controlling doze.
*/
+@DozeScope
public class DozeUi implements DozeMachine.Part {
private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min
@@ -45,7 +50,7 @@
private final DozeHost mHost;
private final Handler mHandler;
private final WakeLock mWakeLock;
- private final DozeMachine mMachine;
+ private DozeMachine mMachine;
private final AlarmTimeout mTimeTicker;
private final boolean mCanAnimateTransition;
private final DozeParameters mDozeParameters;
@@ -64,12 +69,12 @@
private long mLastTimeTickElapsed = 0;
- public DozeUi(Context context, AlarmManager alarmManager, DozeMachine machine,
- WakeLock wakeLock, DozeHost host, Handler handler,
+ @Inject
+ public DozeUi(Context context, AlarmManager alarmManager,
+ WakeLock wakeLock, DozeHost host, @Main Handler handler,
DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor,
DozeLog dozeLog) {
mContext = context;
- mMachine = machine;
mWakeLock = wakeLock;
mHost = host;
mHandler = handler;
@@ -80,6 +85,11 @@
mDozeLog = dozeLog;
}
+ @Override
+ public void setDozeMachine(DozeMachine dozeMachine) {
+ mMachine = dozeMachine;
+ }
+
/**
* Decide if we're taking over the screen-off animation
* when the device was configured to skip doze after screen off.
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
index 7aeb785..d5b6cb1 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
@@ -21,15 +21,19 @@
import android.os.RemoteException;
import android.util.Log;
+import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters;
import java.io.PrintWriter;
+import javax.inject.Inject;
+
/**
* Propagates doze state to wallpaper engine.
*/
+@DozeScope
public class DozeWallpaperState implements DozeMachine.Part {
private static final String TAG = "DozeWallpaperState";
@@ -41,8 +45,9 @@
private final BiometricUnlockController mBiometricUnlockController;
private boolean mIsAmbientMode;
+ @Inject
public DozeWallpaperState(
- IWallpaperManager wallpaperManagerService,
+ @Nullable IWallpaperManager wallpaperManagerService,
BiometricUnlockController biometricUnlockController,
DozeParameters parameters) {
mWallpaperManagerService = wallpaperManagerService;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/BrightnessSensor.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/BrightnessSensor.java
new file mode 100644
index 0000000..5af8af3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/BrightnessSensor.java
@@ -0,0 +1,30 @@
+/*
+ * 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.doze.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface BrightnessSensor {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java
new file mode 100644
index 0000000..e925927
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.doze.dagger;
+
+import com.android.systemui.doze.DozeMachine;
+import com.android.systemui.doze.DozeService;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * Dagger component for items that require a {@link DozeService}.
+ */
+@Subcomponent(modules = {DozeModule.class})
+@DozeScope
+public interface DozeComponent {
+ /** Simple Builder for {@link DozeComponent}. */
+ @Subcomponent.Factory
+ interface Builder {
+ DozeComponent build(@BindsInstance DozeService dozeService);
+ }
+
+ /** Supply a {@link DozeMachine}. */
+ @DozeScope
+ DozeMachine getDozeMachine();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
new file mode 100644
index 0000000..a12e280
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
@@ -0,0 +1,99 @@
+/*
+ * 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.doze.dagger;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.os.Handler;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.doze.DozeAuthRemover;
+import com.android.systemui.doze.DozeBrightnessHostForwarder;
+import com.android.systemui.doze.DozeDockHandler;
+import com.android.systemui.doze.DozeFalsingManagerAdapter;
+import com.android.systemui.doze.DozeHost;
+import com.android.systemui.doze.DozeMachine;
+import com.android.systemui.doze.DozePauser;
+import com.android.systemui.doze.DozeScreenBrightness;
+import com.android.systemui.doze.DozeScreenState;
+import com.android.systemui.doze.DozeScreenStatePreventingAdapter;
+import com.android.systemui.doze.DozeSensors;
+import com.android.systemui.doze.DozeService;
+import com.android.systemui.doze.DozeSuspendScreenStatePreventingAdapter;
+import com.android.systemui.doze.DozeTriggers;
+import com.android.systemui.doze.DozeUi;
+import com.android.systemui.doze.DozeWallpaperState;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.wakelock.DelayedWakeLock;
+import com.android.systemui.util.wakelock.WakeLock;
+
+import dagger.Module;
+import dagger.Provides;
+
+/** Dagger module for use with {@link com.android.systemui.doze.dagger.DozeComponent}. */
+@Module
+public abstract class DozeModule {
+ @Provides
+ @DozeScope
+ @WrappedService
+ static DozeMachine.Service providesWrappedService(DozeService dozeService, DozeHost dozeHost,
+ DozeParameters dozeParameters) {
+ DozeMachine.Service wrappedService = dozeService;
+ wrappedService = new DozeBrightnessHostForwarder(wrappedService, dozeHost);
+ wrappedService = DozeScreenStatePreventingAdapter.wrapIfNeeded(
+ wrappedService, dozeParameters);
+ wrappedService = DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(
+ wrappedService, dozeParameters);
+
+ return wrappedService;
+ }
+
+ @Provides
+ @DozeScope
+ static WakeLock providesDozeWakeLock(DelayedWakeLock.Builder delayedWakeLockBuilder,
+ @Main Handler handler) {
+ return delayedWakeLockBuilder.setHandler(handler).setTag("Doze").build();
+ }
+
+ @Provides
+ static DozeMachine.Part[] providesDozeMachinePartes(DozePauser dozePauser,
+ DozeFalsingManagerAdapter dozeFalsingManagerAdapter, DozeTriggers dozeTriggers,
+ DozeUi dozeUi, DozeScreenState dozeScreenState,
+ DozeScreenBrightness dozeScreenBrightness, DozeWallpaperState dozeWallpaperState,
+ DozeDockHandler dozeDockHandler, DozeAuthRemover dozeAuthRemover) {
+ return new DozeMachine.Part[]{
+ dozePauser,
+ dozeFalsingManagerAdapter,
+ dozeTriggers,
+ dozeUi,
+ dozeScreenState,
+ dozeScreenBrightness,
+ dozeWallpaperState,
+ dozeDockHandler,
+ dozeAuthRemover
+ };
+ }
+
+ @Provides
+ @BrightnessSensor
+ static Sensor providesBrightnessSensor(AsyncSensorManager sensorManager, Context context) {
+ return DozeSensors.findSensorWithType(sensorManager,
+ context.getString(R.string.doze_brightness_sensor_type));
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java
new file mode 100644
index 0000000..7a8b816
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.doze.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Scope;
+
+/**
+ * Scope annotation for singleton items within the StatusBarComponent.
+ */
+@Documented
+@Retention(RUNTIME)
+@Scope
+public @interface DozeScope {}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/WrappedService.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/WrappedService.java
new file mode 100644
index 0000000..ca8a158
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/WrappedService.java
@@ -0,0 +1,30 @@
+/*
+ * 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.doze.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface WrappedService {
+}
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/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 127c5dd..e12b7dd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -5,6 +5,7 @@
import android.content.res.Configuration
import android.graphics.Color
import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
+import android.util.Log
import android.util.MathUtils
import android.view.LayoutInflater
import android.view.View
@@ -25,6 +26,7 @@
import javax.inject.Provider
import javax.inject.Singleton
+private const val TAG = "MediaCarouselController"
private val settingsIntent = Intent().setAction(ACTION_MEDIA_CONTROLS_SETTINGS)
/**
@@ -236,7 +238,9 @@
val oldData = mediaPlayers[oldKey]
if (oldData != null) {
val oldData = mediaPlayers.remove(oldKey)
- mediaPlayers.put(key, oldData!!)
+ mediaPlayers.put(key, oldData!!)?.let {
+ Log.wtf(TAG, "new key $key already exists when migrating from $oldKey")
+ }
}
var existingPlayer = mediaPlayers[key]
if (existingPlayer == null) {
@@ -271,6 +275,11 @@
updatePageIndicator()
mediaCarouselScrollHandler.onPlayersChanged()
mediaCarousel.requiresRemeasuring = true
+ // Check postcondition: mediaContent should have the same number of children as there are
+ // elements in mediaPlayers.
+ if (mediaPlayers.size != mediaContent.childCount) {
+ Log.wtf(TAG, "Size of players list and number of views in carousel are out of sync")
+ }
}
private fun removePlayer(key: String) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
index 0904ebc..d0642cc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
@@ -33,23 +33,31 @@
init {
dataSource.addListener(object : MediaDataManager.Listener {
override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
- if (oldKey != null && !oldKey.equals(key)) {
- val s = entries[oldKey]?.second
- entries[key] = data to entries[oldKey]?.second
- entries.remove(oldKey)
+ if (oldKey != null && oldKey != key && entries.contains(oldKey)) {
+ entries[key] = data to entries.remove(oldKey)?.second
+ update(key, oldKey)
} else {
entries[key] = data to entries[key]?.second
+ update(key, key)
}
- update(key, oldKey)
}
override fun onMediaDataRemoved(key: String) {
remove(key)
}
})
deviceSource.addListener(object : MediaDeviceManager.Listener {
- override fun onMediaDeviceChanged(key: String, data: MediaDeviceData?) {
- entries[key] = entries[key]?.first to data
- update(key, key)
+ override fun onMediaDeviceChanged(
+ key: String,
+ oldKey: String?,
+ data: MediaDeviceData?
+ ) {
+ if (oldKey != null && oldKey != key && entries.contains(oldKey)) {
+ entries[key] = entries.remove(oldKey)?.first to data
+ update(key, oldKey)
+ } else {
+ entries[key] = entries[key]?.first to data
+ update(key, key)
+ }
}
override fun onKeyRemoved(key: String) {
remove(key)
@@ -62,9 +70,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/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 8cb93bf..299ae5b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -517,22 +517,36 @@
fun onNotificationRemoved(key: String) {
Assert.isMainThread()
- if (useMediaResumption && mediaEntries.get(key)?.resumeAction != null) {
+ val removed = mediaEntries.remove(key)
+ if (useMediaResumption && removed?.resumeAction != null) {
Log.d(TAG, "Not removing $key because resumable")
- // Move to resume key aka package name
- val data = mediaEntries.remove(key)!!
- val resumeAction = getResumeMediaAction(data.resumeAction!!)
- val updated = data.copy(token = null, actions = listOf(resumeAction),
+ // Move to resume key (aka package name) if that key doesn't already exist.
+ val resumeAction = getResumeMediaAction(removed.resumeAction!!)
+ val updated = removed.copy(token = null, actions = listOf(resumeAction),
actionsToShowInCompact = listOf(0), active = false, resumption = true)
- mediaEntries.put(data.packageName, updated)
- // Notify listeners of "new" controls
+ val pkg = removed?.packageName
+ val migrate = mediaEntries.put(pkg, updated) == null
+ // Notify listeners of "new" controls when migrating or removed and update when not
val listenersCopy = listeners.toSet()
- listenersCopy.forEach {
- it.onMediaDataLoaded(data.packageName, key, updated)
+ if (migrate) {
+ listenersCopy.forEach {
+ it.onMediaDataLoaded(pkg, key, updated)
+ }
+ } else {
+ // Since packageName is used for the key of the resumption controls, it is
+ // possible that another notification has already been reused for the resumption
+ // controls of this package. In this case, rather than renaming this player as
+ // packageName, just remove it and then send a update to the existing resumption
+ // controls.
+ listenersCopy.forEach {
+ it.onMediaDataRemoved(key)
+ }
+ listenersCopy.forEach {
+ it.onMediaDataLoaded(pkg, pkg, updated)
+ }
}
return
}
- val removed = mediaEntries.remove(key)
if (removed != null) {
val listenersCopy = listeners.toSet()
listenersCopy.forEach {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index 7ae2dc5..143f849 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -71,7 +71,8 @@
val controller = data.token?.let {
MediaController(context, it)
}
- entry = Token(key, controller, localMediaManagerFactory.create(data.packageName))
+ entry = Token(key, oldKey, controller,
+ localMediaManagerFactory.create(data.packageName))
entries[key] = entry
entry.start()
}
@@ -98,23 +99,24 @@
}
}
- private fun processDevice(key: String, device: MediaDevice?) {
+ private fun processDevice(key: String, oldKey: String?, device: MediaDevice?) {
val enabled = device != null
val data = MediaDeviceData(enabled, device?.iconWithoutBackground, device?.name)
listeners.forEach {
- it.onMediaDeviceChanged(key, data)
+ it.onMediaDeviceChanged(key, oldKey, data)
}
}
interface Listener {
/** Called when the route has changed for a given notification. */
- fun onMediaDeviceChanged(key: String, data: MediaDeviceData?)
+ fun onMediaDeviceChanged(key: String, oldKey: String?, data: MediaDeviceData?)
/** Called when the notification was removed. */
fun onKeyRemoved(key: String)
}
private inner class Token(
val key: String,
+ val oldKey: String?,
val controller: MediaController?,
val localMediaManager: LocalMediaManager
) : LocalMediaManager.DeviceCallback {
@@ -125,7 +127,7 @@
set(value) {
if (!started || value != field) {
field = value
- processDevice(key, value)
+ processDevice(key, oldKey, value)
}
}
fun start() {
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/onehanded/OneHandedAnimationCallback.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java
index 5d9439f..c581ac6 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java
@@ -16,6 +16,8 @@
package com.android.systemui.onehanded;
+import android.view.SurfaceControl;
+
/**
* Additional callback interface for OneHanded animation
*/
@@ -30,7 +32,7 @@
/**
* Called when OneHanded animation is ended.
*/
- default void onOneHandedAnimationEnd(
+ default void onOneHandedAnimationEnd(SurfaceControl.Transaction tx,
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java
index 08f08c5..1926c44 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java
@@ -19,16 +19,16 @@
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
-import android.content.Context;
import android.graphics.Rect;
import android.view.SurfaceControl;
-import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import android.view.animation.OvershootInterpolator;
import androidx.annotation.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
import javax.inject.Inject;
@@ -39,18 +39,6 @@
private static final float FRACTION_START = 0f;
private static final float FRACTION_END = 1f;
- public static final int ANIM_TYPE_TRANSLATE = 0;
- public static final int ANIM_TYPE_SCALE = 1;
-
- // Note: ANIM_TYPE_SCALE reserve for the future development
- @IntDef(prefix = {"ANIM_TYPE_"}, value = {
- ANIM_TYPE_TRANSLATE,
- ANIM_TYPE_SCALE
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface AnimationType {
- }
-
public static final int TRANSITION_DIRECTION_NONE = 0;
public static final int TRANSITION_DIRECTION_TRIGGER = 1;
public static final int TRANSITION_DIRECTION_EXIT = 2;
@@ -64,42 +52,57 @@
public @interface TransitionDirection {
}
- private final Interpolator mFastOutSlowInInterpolator;
+ private final Interpolator mOvershootInterpolator;
private final OneHandedSurfaceTransactionHelper mSurfaceTransactionHelper;
- private OneHandedTransitionAnimator mCurrentAnimator;
+ private final HashMap<SurfaceControl, OneHandedTransitionAnimator> mAnimatorMap =
+ new HashMap<>();
/**
* Constructor of OneHandedAnimationController
*/
@Inject
- public OneHandedAnimationController(Context context,
+ public OneHandedAnimationController(
OneHandedSurfaceTransactionHelper surfaceTransactionHelper) {
mSurfaceTransactionHelper = surfaceTransactionHelper;
- mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.fast_out_slow_in);
+ mOvershootInterpolator = new OvershootInterpolator();
}
@SuppressWarnings("unchecked")
OneHandedTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds,
Rect endBounds) {
- if (mCurrentAnimator == null) {
- mCurrentAnimator = setupOneHandedTransitionAnimator(
- OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds));
- } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_TRANSLATE
- && mCurrentAnimator.isRunning()) {
- mCurrentAnimator.updateEndValue(endBounds);
+ final OneHandedTransitionAnimator animator = mAnimatorMap.get(leash);
+ if (animator == null) {
+ mAnimatorMap.put(leash, setupOneHandedTransitionAnimator(
+ OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds)));
+ } else if (animator.isRunning()) {
+ animator.updateEndValue(endBounds);
} else {
- mCurrentAnimator.cancel();
- mCurrentAnimator = setupOneHandedTransitionAnimator(
- OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds));
+ animator.cancel();
+ mAnimatorMap.put(leash, setupOneHandedTransitionAnimator(
+ OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds)));
}
- return mCurrentAnimator;
+ return mAnimatorMap.get(leash);
+ }
+
+ HashMap<SurfaceControl, OneHandedTransitionAnimator> getAnimatorMap() {
+ return mAnimatorMap;
+ }
+
+ boolean isAnimatorsConsumed() {
+ return mAnimatorMap.isEmpty();
+ }
+
+ void removeAnimator(SurfaceControl key) {
+ final OneHandedTransitionAnimator animator = mAnimatorMap.remove(key);
+ if (animator != null && animator.isRunning()) {
+ animator.cancel();
+ }
}
OneHandedTransitionAnimator setupOneHandedTransitionAnimator(
OneHandedTransitionAnimator animator) {
animator.setSurfaceTransactionHelper(mSurfaceTransactionHelper);
- animator.setInterpolator(mFastOutSlowInInterpolator);
+ animator.setInterpolator(mOvershootInterpolator);
animator.setFloatValues(FRACTION_START, FRACTION_END);
return animator;
}
@@ -114,7 +117,6 @@
ValueAnimator.AnimatorListener {
private final SurfaceControl mLeash;
- private final @AnimationType int mAnimationType;
private T mStartValue;
private T mEndValue;
private T mCurrentValue;
@@ -127,10 +129,8 @@
private @TransitionDirection int mTransitionDirection;
private int mTransitionOffset;
- private OneHandedTransitionAnimator(SurfaceControl leash, @AnimationType int animationType,
- T startValue, T endValue) {
+ private OneHandedTransitionAnimator(SurfaceControl leash, T startValue, T endValue) {
mLeash = leash;
- mAnimationType = animationType;
mStartValue = startValue;
mEndValue = endValue;
addListener(this);
@@ -150,8 +150,10 @@
@Override
public void onAnimationEnd(Animator animation) {
mCurrentValue = mEndValue;
+ final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
+ onEndTransaction(mLeash, tx);
if (mOneHandedAnimationCallback != null) {
- mOneHandedAnimationCallback.onOneHandedAnimationEnd(this);
+ mOneHandedAnimationCallback.onOneHandedAnimationEnd(tx, this);
}
}
@@ -196,6 +198,10 @@
return this;
}
+ SurfaceControl getLeash() {
+ return mLeash;
+ }
+
Rect getDestinationBounds() {
return (Rect) mEndValue;
}
@@ -227,11 +233,6 @@
return mEndValue;
}
- @AnimationType
- int getAnimationType() {
- return mAnimationType;
- }
-
void setCurrentValue(T value) {
mCurrentValue = value;
}
@@ -250,11 +251,9 @@
@VisibleForTesting
static OneHandedTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
Rect startValue, Rect endValue) {
- // At R, we only support translate type first.
- final int animType = ANIM_TYPE_TRANSLATE;
- return new OneHandedTransitionAnimator<Rect>(leash, animType,
- new Rect(startValue), new Rect(endValue)) {
+ return new OneHandedTransitionAnimator<Rect>(leash, new Rect(startValue),
+ new Rect(endValue)) {
private final Rect mTmpRect = new Rect();
@@ -282,7 +281,7 @@
void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
getSurfaceTransactionHelper()
.alpha(tx, leash, 1f)
- .crop(tx, leash, getStartValue())
+ .translate(tx, leash, getEndValue().top - getStartValue().top)
.round(tx, leash);
tx.apply();
}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
index 063d7c8..28d7049 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -22,11 +22,14 @@
import static com.android.systemui.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
+import android.view.Display;
+import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.window.DisplayAreaInfo;
import android.window.DisplayAreaOrganizer;
@@ -39,10 +42,12 @@
import com.android.internal.os.SomeArgs;
import com.android.systemui.Dumpable;
import com.android.systemui.wm.DisplayController;
+import com.android.systemui.wm.DisplayLayout;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Objects;
@@ -68,22 +73,22 @@
@VisibleForTesting
static final int MSG_OFFSET_FINISH = 3;
- private final Handler mUpdateHandler;
+ private final Rect mLastVisualDisplayBounds = new Rect();
+ private final Rect mDefaultDisplayBounds = new Rect();
+ private Handler mUpdateHandler;
private boolean mIsInOneHanded;
private int mEnterExitAnimationDurationMs;
@VisibleForTesting
- DisplayAreaInfo mDisplayAreaInfo;
+ HashMap<DisplayAreaInfo, SurfaceControl> mDisplayAreaMap = new HashMap();
private DisplayController mDisplayController;
+ private DisplayLayout mDisplayLayout;
+ private DisplayInfo mDisplayInfo = new DisplayInfo();
private OneHandedAnimationController mAnimationController;
- private SurfaceControl mLeash;
- private List<OneHandedTransitionCallback> mTransitionCallbacks = new ArrayList<>();
- private OneHandedSurfaceTransactionHelper mSurfaceTransactionHelper;
private OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
- private Rect mLastVisualDisplayBounds;
- private Rect mDefaultDisplayBounds;
+ private List<OneHandedTransitionCallback> mTransitionCallbacks = new ArrayList<>();
@VisibleForTesting
OneHandedAnimationCallback mOneHandedAnimationCallback =
@@ -94,17 +99,23 @@
}
@Override
- public void onOneHandedAnimationEnd(
+ public void onOneHandedAnimationEnd(SurfaceControl.Transaction tx,
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
- mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH,
- obtainArgsFromAnimator(animator)));
+ mAnimationController.removeAnimator(animator.getLeash());
+ if (mAnimationController.isAnimatorsConsumed()) {
+ mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH,
+ obtainArgsFromAnimator(animator)));
+ }
}
@Override
public void onOneHandedAnimationCancel(
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
- mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH,
- obtainArgsFromAnimator(animator)));
+ mAnimationController.removeAnimator(animator.getLeash());
+ if (mAnimationController.isAnimatorsConsumed()) {
+ mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH,
+ obtainArgsFromAnimator(animator)));
+ }
}
};
@@ -112,34 +123,24 @@
private Handler.Callback mUpdateCallback = (msg) -> {
SomeArgs args = (SomeArgs) msg.obj;
final Rect currentBounds = args.arg1 != null ? (Rect) args.arg1 : mDefaultDisplayBounds;
- final int xOffset = args.argi1;
final int yOffset = args.argi2;
final int direction = args.argi3;
- if (mLeash == null || !mLeash.isValid()) {
- Log.w(TAG, "mLeash is null, abort transition");
- return false;
- }
switch (msg.what) {
case MSG_RESET_IMMEDIATE:
- final OneHandedAnimationController.OneHandedTransitionAnimator animator =
- mAnimationController.getAnimator(mLeash, currentBounds,
- mDefaultDisplayBounds);
- if (animator != null && animator.isRunning()) {
- animator.cancel();
- }
- final SurfaceControl.Transaction tx =
- mSurfaceControlTransactionFactory.getTransaction();
- tx.setPosition(mLeash, 0, 0)
- .setWindowCrop(mLeash, -1/* reset */, -1/* reset */)
- .apply();
- finishOffset(currentBounds, yOffset, direction);
+ resetWindowsOffset();
+ mLastVisualDisplayBounds.set(currentBounds);
+ finishOffset(0, TRANSITION_DIRECTION_EXIT);
break;
case MSG_OFFSET_ANIMATE:
- offsetWindows(currentBounds, 0, yOffset, direction, mEnterExitAnimationDurationMs);
+ final Rect toBounds = new Rect(mDefaultDisplayBounds.left,
+ mDefaultDisplayBounds.top + yOffset,
+ mDefaultDisplayBounds.right,
+ mDefaultDisplayBounds.bottom + yOffset);
+ offsetWindows(currentBounds, toBounds, direction, mEnterExitAnimationDurationMs);
break;
case MSG_OFFSET_FINISH:
- finishOffset(currentBounds, yOffset, direction);
+ finishOffset(yOffset, direction);
break;
}
args.recycle();
@@ -152,17 +153,15 @@
@Inject
public OneHandedDisplayAreaOrganizer(Context context,
DisplayController displayController,
- OneHandedAnimationController animationController,
- OneHandedSurfaceTransactionHelper surfaceTransactionHelper) {
-
+ OneHandedAnimationController animationController) {
mUpdateHandler = new Handler(OneHandedThread.get().getLooper(), mUpdateCallback);
mAnimationController = animationController;
mDisplayController = displayController;
+ mDisplayLayout = getDisplayLayout(context);
+ mDisplayLayout.getStableBounds(mDefaultDisplayBounds);
+ mLastVisualDisplayBounds.set(mDefaultDisplayBounds);
mEnterExitAnimationDurationMs = context.getResources().getInteger(
com.android.systemui.R.integer.config_one_handed_translate_animation_duration);
- mDefaultDisplayBounds = new Rect(0, 0, getDisplayBounds().x, getDisplayBounds().y);
- mLastVisualDisplayBounds = new Rect(mDefaultDisplayBounds);
- mSurfaceTransactionHelper = surfaceTransactionHelper;
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
}
@@ -171,40 +170,52 @@
@NonNull SurfaceControl leash) {
Objects.requireNonNull(displayAreaInfo, "displayAreaInfo must not be null");
Objects.requireNonNull(leash, "leash must not be null");
- mDisplayAreaInfo = displayAreaInfo;
- mLeash = leash;
- }
- @Override
- public void onDisplayAreaInfoChanged(@NonNull DisplayAreaInfo displayAreaInfo) {
- Objects.requireNonNull(displayAreaInfo, "displayArea must not be null");
- // Stop one handed without animation and reset cropped size immediately
- if (displayAreaInfo.configuration.orientation
- != mDisplayAreaInfo.configuration.orientation) {
- final Rect newBounds = displayAreaInfo.configuration.windowConfiguration.getBounds();
- resetImmediately(newBounds);
+ if (displayAreaInfo.featureId != FEATURE_ONE_HANDED) {
+ Log.w(TAG, "Bypass onDisplayAreaAppeared()! displayAreaInfo=" + displayAreaInfo);
+ return;
}
- mDisplayAreaInfo = displayAreaInfo;
+ mDisplayAreaMap.put(displayAreaInfo, leash);
}
@Override
public void onDisplayAreaVanished(@NonNull DisplayAreaInfo displayAreaInfo) {
Objects.requireNonNull(displayAreaInfo,
"Requires valid displayArea, and displayArea must not be null");
- if (displayAreaInfo.token.asBinder() != mDisplayAreaInfo.token.asBinder()) {
+
+ if (!mDisplayAreaMap.containsKey(displayAreaInfo)) {
Log.w(TAG, "Unrecognized token: " + displayAreaInfo.token);
return;
}
- mDisplayAreaInfo = displayAreaInfo;
+ mDisplayAreaMap.remove(displayAreaInfo);
+ }
- // Stop one handed immediately
- if (isInOneHanded()) {
- final Rect newBounds = mDisplayAreaInfo.configuration.windowConfiguration.getBounds();
- resetImmediately(newBounds);
+ @Override
+ public void unregisterOrganizer() {
+ super.unregisterOrganizer();
+ if (mDisplayAreaMap != null) {
+ mDisplayAreaMap.clear();
}
}
/**
+ * Handler for display rotation changes.
+ */
+ public void onRotateDisplay(Resources res, int toRotation) {
+ // Stop one handed without animation and reset cropped size immediately
+ final Rect newBounds = new Rect();
+ mDisplayLayout.rotateTo(res, toRotation);
+ mDisplayLayout.getStableBounds(newBounds);
+
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = newBounds;
+ args.argi1 = 0 /* xOffset */;
+ args.argi2 = 0 /* yOffset */;
+ args.argi3 = TRANSITION_DIRECTION_EXIT;
+ mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESET_IMMEDIATE, args));
+ }
+
+ /**
* Offset the windows by a given offset on Y-axis, triggered also from screen rotation.
* Directly perform manipulation/offset on the leash.
*/
@@ -217,55 +228,43 @@
mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_ANIMATE, args));
}
- /**
- * Immediately resize/reset leash from previous cropped boundary to default.
- * (i.e Screen rotation need to reset crop and position before applying new bounds)
- */
- public void resetImmediately(Rect newDisplayBounds) {
- updateDisplayBounds(newDisplayBounds);
- if (mDisplayAreaInfo != null && mLeash != null) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = newDisplayBounds;
- args.argi1 = 0 /* xOffset */;
- args.argi2 = 0 /* yOffset */;
- args.argi3 = TRANSITION_DIRECTION_EXIT;
- mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESET_IMMEDIATE, args));
- }
- }
-
- private void offsetWindows(Rect currentbounds, int xOffset, int yOffset, int direction,
- int durationMs) {
+ private void offsetWindows(Rect fromBounds, Rect toBounds, int direction, int durationMs) {
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
throw new RuntimeException("Callers should call scheduleOffset() instead of this "
+ "directly");
}
- if (mDisplayAreaInfo == null || mLeash == null) {
- Log.w(TAG, "mToken is not set");
- return;
- }
-
- Rect toBounds = new Rect(mDefaultDisplayBounds.left,
- mDefaultDisplayBounds.top + yOffset,
- mDefaultDisplayBounds.right,
- mDefaultDisplayBounds.bottom + yOffset);
- animateWindows(currentbounds, toBounds, direction, durationMs);
+ mDisplayAreaMap.forEach(
+ (key, leash) -> animateWindows(leash, fromBounds, toBounds, direction,
+ durationMs));
}
- private void animateWindows(Rect fromBounds, Rect toBounds,
+ private void resetWindowsOffset() {
+ mUpdateHandler.post(() -> {
+ final SurfaceControl.Transaction tx =
+ mSurfaceControlTransactionFactory.getTransaction();
+ mDisplayAreaMap.forEach(
+ (key, leash) -> {
+ final OneHandedAnimationController.OneHandedTransitionAnimator animator =
+ mAnimationController.getAnimatorMap().remove(leash);
+ if (animator != null && animator.isRunning()) {
+ animator.cancel();
+ }
+ tx.setPosition(leash, 0, 0)
+ .setWindowCrop(leash, -1/* reset */, -1/* reset */);
+ });
+ tx.apply();
+ });
+ }
+
+ private void animateWindows(SurfaceControl leash, Rect fromBounds, Rect toBounds,
@OneHandedAnimationController.TransitionDirection int direction, int durationMs) {
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
throw new RuntimeException("Callers should call scheduleOffset() instead of "
+ "this directly");
}
- // Could happen when exit one handed
- if (mDisplayAreaInfo == null || mLeash == null) {
- Log.w(TAG, "Abort animation, invalid leash");
- return;
- }
-
mUpdateHandler.post(() -> {
final OneHandedAnimationController.OneHandedTransitionAnimator animator =
- mAnimationController.getAnimator(mLeash, fromBounds, toBounds);
+ mAnimationController.getAnimator(leash, fromBounds, toBounds);
if (animator != null) {
animator.setTransitionDirection(direction)
.setOneHandedAnimationCallback(mOneHandedAnimationCallback)
@@ -275,7 +274,7 @@
});
}
- private void finishOffset(Rect currentBounds, int offset,
+ private void finishOffset(int offset,
@OneHandedAnimationController.TransitionDirection int direction) {
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
throw new RuntimeException(
@@ -284,18 +283,14 @@
// Only finishOffset() can update mIsInOneHanded to ensure the state is handle in sequence,
// the flag *MUST* be updated before dispatch mTransitionCallbacks
mIsInOneHanded = (offset > 0 || direction == TRANSITION_DIRECTION_TRIGGER);
- mLastVisualDisplayBounds.set(currentBounds);
- if (direction == TRANSITION_DIRECTION_TRIGGER) {
- mLastVisualDisplayBounds.offsetTo(0, offset);
- for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) {
- final OneHandedTransitionCallback callback = mTransitionCallbacks.get(i);
- callback.onStartFinished(mLastVisualDisplayBounds);
- }
- } else {
- mLastVisualDisplayBounds.offsetTo(0, 0);
- for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) {
- final OneHandedTransitionCallback callback = mTransitionCallbacks.get(i);
- callback.onStopFinished(mLastVisualDisplayBounds);
+ mLastVisualDisplayBounds.offsetTo(0,
+ direction == TRANSITION_DIRECTION_TRIGGER ? offset : 0);
+ for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) {
+ final OneHandedTransitionCallback callback = mTransitionCallbacks.get(i);
+ if (direction == TRANSITION_DIRECTION_TRIGGER) {
+ callback.onStartFinished(getLastVisualDisplayBounds());
+ } else {
+ callback.onStopFinished(getLastVisualDisplayBounds());
}
}
}
@@ -332,6 +327,16 @@
return mUpdateHandler;
}
+ private DisplayLayout getDisplayLayout(Context context) {
+ final Display display = mDisplayController.getDisplay(DEFAULT_DISPLAY);
+ if (display != null) {
+ display.getDisplayInfo(mDisplayInfo);
+ } else {
+ Log.w(TAG, "get DEFAULT_DISPLAY return null");
+ }
+ return new DisplayLayout(mDisplayInfo, context.getResources(), false, false);
+ }
+
/**
* Register transition callback
*/
@@ -349,29 +354,23 @@
return args;
}
- private void updateDisplayBounds(Rect newDisplayBounds) {
- if (newDisplayBounds == null) {
- mDefaultDisplayBounds.set(0, 0, getDisplayBounds().x, getDisplayBounds().y);
- } else {
- mDefaultDisplayBounds.set(newDisplayBounds);
- }
- }
-
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
final String innerPrefix = " ";
pw.println(TAG + "states: ");
pw.print(innerPrefix + "mIsInOneHanded=");
pw.println(mIsInOneHanded);
- pw.print(innerPrefix + "mDisplayAreaInfo=");
- pw.println(mDisplayAreaInfo);
- pw.print(innerPrefix + "mLeash=");
- pw.println(mLeash);
+ pw.print(innerPrefix + "mDisplayAreaMap=");
+ pw.println(mDisplayAreaMap);
pw.print(innerPrefix + "mDefaultDisplayBounds=");
pw.println(mDefaultDisplayBounds);
pw.print(innerPrefix + "mLastVisualDisplayBounds=");
pw.println(mLastVisualDisplayBounds);
pw.print(innerPrefix + "getDisplayBounds()=");
pw.println(getDisplayBounds());
+ if (mDisplayLayout != null) {
+ pw.print(innerPrefix + "mDisplayLayout(w, h)=");
+ pw.println(mDisplayLayout.width() + ", " + mDisplayLayout.height());
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
index d60b43f..4dacdf3 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
@@ -27,11 +27,13 @@
import androidx.annotation.NonNull;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.model.SysUiState;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.wm.DisplayChangeController;
import com.android.systemui.wm.DisplayController;
import java.io.FileDescriptor;
@@ -50,15 +52,18 @@
private boolean mIsOneHandedEnabled;
private boolean mTaskChangeToExit;
private float mOffSetFraction;
- private DisplayController mDisplayController;
+
+ private final DisplayController mDisplayController;
+ private final OneHandedGestureHandler mGestureHandler;
+ private final OneHandedTimeoutHandler mTimeoutHandler;
+ private final OneHandedTouchHandler mTouchHandler;
+ private final SysUiState mSysUiFlagContainer;
+
+ private Context mContext;
private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
- private OneHandedGestureHandler mGestureHandler;
private OneHandedGestureHandler.OneHandedGestureEventCallback mGestureEventCallback;
- private OneHandedTimeoutHandler mTimeoutHandler;
- private OneHandedTouchHandler mTouchHandler;
private OneHandedTouchHandler.OneHandedTouchEventCallback mTouchEventCallback;
private OneHandedTransitionCallback mTransitionCallback;
- private SysUiState mSysUiFlagContainer;
/**
* Handler for system task stack changes, exit when user lunch new task or bring task to front
@@ -84,6 +89,17 @@
};
/**
+ * Handle rotation based on OnDisplayChangingListener callback
+ */
+ @VisibleForTesting
+ private final DisplayChangeController.OnDisplayChangingListener mRotationController =
+ (display, fromRotation, toRotation, wct) -> {
+ if (mDisplayAreaOrganizer != null) {
+ mDisplayAreaOrganizer.onRotateDisplay(mContext.getResources(), toRotation);
+ }
+ };
+
+ /**
* Constructor of OneHandedManager
*/
@Inject
@@ -93,9 +109,10 @@
OneHandedTouchHandler touchHandler,
OneHandedGestureHandler gestureHandler,
SysUiState sysUiState) {
-
+ mContext = context;
mDisplayAreaOrganizer = displayAreaOrganizer;
mDisplayController = displayController;
+ mDisplayController.addDisplayChangingController(mRotationController);
mSysUiFlagContainer = sysUiState;
mOffSetFraction =
context.getResources().getFraction(R.fraction.config_one_handed_offset, 1, 1);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index b6e4e16..32f8c12 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -171,7 +171,6 @@
private float mSavedSnapFraction = -1f;
private boolean mSendingHoverAccessibilityEvents;
private boolean mMovementWithinDismiss;
- private boolean mHideMenuAfterShown = false;
private PipAccessibilityInteractionConnection mConnection;
// Touch state
@@ -242,7 +241,8 @@
this::updateMovementBounds, sysUiState);
mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
() -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(),
- true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()));
+ true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()),
+ menuController::hideMenu);
Resources res = context.getResources();
mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
@@ -272,7 +272,9 @@
mMagnetizedPip.setAnimateStuckToTarget(
(target, velX, velY, flung, after) -> {
- mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, after);
+ if (mEnableDismissDragToEdge) {
+ mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, after);
+ }
return Unit.INSTANCE;
});
mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() {
@@ -280,7 +282,9 @@
public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
// Show the dismiss target, in case the initial touch event occurred within the
// magnetic field radius.
- showDismissTargetMaybe();
+ if (mEnableDismissDragToEdge) {
+ showDismissTargetMaybe();
+ }
}
@Override
@@ -708,6 +712,7 @@
// on and changing MotionEvents into HoverEvents.
// Let's not enable menu show/hide for a11y services.
if (!mAccessibilityManager.isTouchExplorationEnabled()) {
+ mTouchState.removeHoverExitTimeoutCallback();
mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
false /* allowMenuTimeout */, false /* willResizeMenu */,
shouldShowResizeHandle());
@@ -724,8 +729,7 @@
// on and changing MotionEvents into HoverEvents.
// Let's not enable menu show/hide for a11y services.
if (!mAccessibilityManager.isTouchExplorationEnabled()) {
- mHideMenuAfterShown = true;
- mMenuController.hideMenu();
+ mTouchState.scheduleHoverExitTimeoutCallback();
}
if (!shouldDeliverToMenu && mSendingHoverAccessibilityEvents) {
sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
@@ -811,9 +815,6 @@
mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
mMovementBounds, mExpandedMovementBounds, callback);
}
- if (mHideMenuAfterShown) {
- mMenuController.hideMenu();
- }
} else if (menuState == MENU_STATE_NONE && mMenuState == MENU_STATE_FULL) {
// Try and restore the PiP to the closest edge, using the saved snap fraction
// if possible
@@ -851,7 +852,6 @@
}
}
mMenuState = menuState;
- mHideMenuAfterShown = false;
updateMovementBounds();
// If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip
// as well, or it can't handle a11y focus and pip menu can't perform any action.
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
index 132c04d..ecd1128a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
@@ -36,10 +36,12 @@
@VisibleForTesting
static final long DOUBLE_TAP_TIMEOUT = 200;
+ static final long HOVER_EXIT_TIMEOUT = 50;
private final Handler mHandler;
private final ViewConfiguration mViewConfig;
private final Runnable mDoubleTapTimeoutCallback;
+ private final Runnable mHoverExitTimeoutCallback;
private VelocityTracker mVelocityTracker;
private long mDownTouchTime = 0;
@@ -64,10 +66,11 @@
private int mActivePointerId;
public PipTouchState(ViewConfiguration viewConfig, Handler handler,
- Runnable doubleTapTimeoutCallback) {
+ Runnable doubleTapTimeoutCallback, Runnable hoverExitTimeoutCallback) {
mViewConfig = viewConfig;
mHandler = handler;
mDoubleTapTimeoutCallback = doubleTapTimeoutCallback;
+ mHoverExitTimeoutCallback = hoverExitTimeoutCallback;
}
/**
@@ -197,6 +200,10 @@
recycleVelocityTracker();
break;
}
+ case MotionEvent.ACTION_BUTTON_PRESS: {
+ removeHoverExitTimeoutCallback();
+ break;
+ }
}
}
@@ -326,6 +333,15 @@
mHandler.removeCallbacks(mDoubleTapTimeoutCallback);
}
+ void scheduleHoverExitTimeoutCallback() {
+ mHandler.removeCallbacks(mHoverExitTimeoutCallback);
+ mHandler.postDelayed(mHoverExitTimeoutCallback, HOVER_EXIT_TIMEOUT);
+ }
+
+ void removeHoverExitTimeoutCallback() {
+ mHandler.removeCallbacks(mHoverExitTimeoutCallback);
+ }
+
void addMovementToVelocityTracker(MotionEvent event) {
if (mVelocityTracker == null) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
index c454048..daf8ca3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
@@ -104,4 +104,13 @@
public void showBackground() {
mBackground.showSecondLayer();
}
+
+ /**
+ * Cancels the animator if it's running.
+ */
+ public void cancelAnimator() {
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ }
+ }
}
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/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 3e2f9de..e5ed88c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -208,6 +208,7 @@
public void showImmediately() {
if (!isShown) {
setVisibility(VISIBLE);
+ mClipper.cancelAnimator();
mClipper.showBackground();
isShown = true;
setTileSpecs();
@@ -230,6 +231,10 @@
mUiEventLogger.log(QSEditEvent.QS_EDIT_CLOSED);
isShown = false;
mToolbar.dismissPopupMenus();
+ mClipper.cancelAnimator();
+ // Make sure we're not opening (because we're closing). Nobody can think we are
+ // customizing after the next two lines.
+ mOpening = false;
setCustomizing(false);
save();
if (animate) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index fba7a22..2ef6934 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.content.Intent;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.util.AttributeSet;
@@ -33,6 +32,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.systemui.R;
import com.android.systemui.qs.PseudoGridView;
import com.android.systemui.qs.QSUserSwitcherEvent;
@@ -100,7 +100,8 @@
if (item.picture == null) {
v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId());
} else {
- Drawable drawable = new BitmapDrawable(v.getResources(), item.picture);
+ int avatarSize = (int) v.getResources().getDimension(R.dimen.qs_framed_avatar_size);
+ Drawable drawable = new CircleFramedDrawable(item.picture, avatarSize);
drawable.setColorFilter(
item.isSwitchToEnabled ? null : getDisabledUserAvatarColorFilter());
v.bind(name, drawable, item.info.id);
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/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 6f554e6..b6c6afd 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -675,6 +675,9 @@
}
private void notifySplitScreenBoundsChanged() {
+ if (mSplitLayout.mPrimary == null || mSplitLayout.mSecondary == null) {
+ return;
+ }
mOtherTaskRect.set(mSplitLayout.mSecondary);
mTmpRect.set(mHandle.getLeft(), mHandle.getTop(), mHandle.getRight(), mHandle.getBottom());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 423f85f..bd65ef0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -282,7 +282,7 @@
+ " doesn't match existing key " + mKey);
}
- mRanking = ranking;
+ mRanking = ranking.withAudiblyAlertedInfo(mRanking);
}
/*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 673aa39..85a3bc9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -142,7 +142,6 @@
.expandableNotificationRow(row)
.notificationEntry(entry)
.onDismissRunnable(onDismissRunnable)
- .rowContentBindStage(mRowContentBindStage)
.onExpandClickListener(mPresenter)
.build();
ExpandableNotificationRowController rowController =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 582e3e5..a7d83b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -37,6 +37,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ImageMessageConsumer;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.media.MediaDataManagerKt;
+import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.SmartReplyController;
@@ -71,6 +73,7 @@
public static final String TAG = "NotifContentInflater";
private boolean mInflateSynchronously = false;
+ private final boolean mIsMediaInQS;
private final NotificationRemoteInputManager mRemoteInputManager;
private final NotifRemoteViewCache mRemoteViewCache;
private final Lazy<SmartReplyConstants> mSmartReplyConstants;
@@ -85,12 +88,14 @@
Lazy<SmartReplyConstants> smartReplyConstants,
Lazy<SmartReplyController> smartReplyController,
ConversationNotificationProcessor conversationProcessor,
+ MediaFeatureFlag mediaFeatureFlag,
@Background Executor bgExecutor) {
mRemoteViewCache = remoteViewCache;
mRemoteInputManager = remoteInputManager;
mSmartReplyConstants = smartReplyConstants;
mSmartReplyController = smartReplyController;
mConversationProcessor = conversationProcessor;
+ mIsMediaInQS = mediaFeatureFlag.getEnabled();
mBgExecutor = bgExecutor;
}
@@ -135,7 +140,8 @@
bindParams.usesIncreasedHeight,
bindParams.usesIncreasedHeadsUpHeight,
callback,
- mRemoteInputManager.getRemoteViewsOnClickHandler());
+ mRemoteInputManager.getRemoteViewsOnClickHandler(),
+ mIsMediaInQS);
if (mInflateSynchronously) {
task.onPostExecute(task.doInBackground());
} else {
@@ -711,6 +717,7 @@
private RemoteViews.OnClickHandler mRemoteViewClickHandler;
private CancellationSignal mCancellationSignal;
private final ConversationNotificationProcessor mConversationProcessor;
+ private final boolean mIsMediaInQS;
private AsyncInflationTask(
Executor bgExecutor,
@@ -726,7 +733,8 @@
boolean usesIncreasedHeight,
boolean usesIncreasedHeadsUpHeight,
InflationCallback callback,
- RemoteViews.OnClickHandler remoteViewClickHandler) {
+ RemoteViews.OnClickHandler remoteViewClickHandler,
+ boolean isMediaFlagEnabled) {
mEntry = entry;
mRow = row;
mSmartReplyConstants = smartReplyConstants;
@@ -742,6 +750,7 @@
mRemoteViewClickHandler = remoteViewClickHandler;
mCallback = callback;
mConversationProcessor = conversationProcessor;
+ mIsMediaInQS = isMediaFlagEnabled;
entry.setInflationTask(this);
}
@@ -765,7 +774,8 @@
packageContext = new RtlEnabledContext(packageContext);
}
Notification notification = sbn.getNotification();
- if (notification.isMediaNotification()) {
+ if (notification.isMediaNotification() && !(mIsMediaInQS
+ && MediaDataManagerKt.isMediaNotification(sbn))) {
MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
packageContext);
processor.processNotification(notification, recoveredBuilder);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
index 9846f2d..321656d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
@@ -25,7 +25,6 @@
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
-import com.android.systemui.statusbar.notification.row.RowContentBindStage;
import com.android.systemui.statusbar.phone.StatusBar;
import dagger.Binds;
@@ -57,8 +56,6 @@
@BindsInstance
Builder onDismissRunnable(@DismissRunnable Runnable runnable);
@BindsInstance
- Builder rowContentBindStage(RowContentBindStage rowContentBindStage);
- @BindsInstance
Builder onExpandClickListener(ExpandableNotificationRow.OnExpandClickListener presenter);
ExpandableNotificationRowComponent build();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index f5999f5..f5ea1c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -211,8 +211,4 @@
public void onTuningChanged(String key, String newValue) {
mDozeAlwaysOn = mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT);
}
-
- public AlwaysOnDisplayPolicy getPolicy() {
- return mAlwaysOnPolicy;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 304fe00..539158c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -614,8 +614,10 @@
// Bubble controller will give us a valid display id if it should get the back event
BubbleController bubbleController = Dependency.get(BubbleController.class);
int bubbleDisplayId = bubbleController.getExpandedDisplayId(mContext);
- if (code == KeyEvent.KEYCODE_BACK && bubbleDisplayId != INVALID_DISPLAY) {
+ if (bubbleDisplayId != INVALID_DISPLAY) {
ev.setDisplayId(bubbleDisplayId);
+ } else {
+ ev.setDisplayId(mContext.getDisplay().getDisplayId());
}
InputManager.getInstance().injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
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/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
index 2e4a929d..5164440 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
@@ -84,6 +84,7 @@
private final boolean mKeyguardScreenRotation;
private final long mLockScreenDisplayTimeout;
private final Display.Mode mKeyguardDisplayMode;
+ private final KeyguardViewMediator mKeyguardViewMediator;
private final KeyguardBypassController mKeyguardBypassController;
private ViewGroup mNotificationShadeView;
private LayoutParams mLp;
@@ -104,6 +105,7 @@
IActivityManager activityManager, DozeParameters dozeParameters,
StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
+ KeyguardViewMediator keyguardViewMediator,
KeyguardBypassController keyguardBypassController, SysuiColorExtractor colorExtractor,
DumpManager dumpManager) {
mContext = context;
@@ -113,6 +115,7 @@
mDozeParameters = dozeParameters;
mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
mLpChanged = new LayoutParams();
+ mKeyguardViewMediator = keyguardViewMediator;
mKeyguardBypassController = keyguardBypassController;
mColorExtractor = colorExtractor;
dumpManager.registerDumpable(getClass().getName(), this);
@@ -202,6 +205,11 @@
mWindowManager.addView(mNotificationShadeView, mLp);
mLpChanged.copyFrom(mLp);
onThemeChanged();
+
+ // Make the state consistent with KeyguardViewMediator#setupLocked during initialization.
+ if (mKeyguardViewMediator.isShowingAndNotOccluded()) {
+ setKeyguardShowing(true);
+ }
}
public void setNotificationShadeView(ViewGroup view) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 732f25f..e942d85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -371,12 +371,26 @@
float vectorVel = (float) Math.hypot(
mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
- boolean expand = flingExpands(vel, vectorVel, x, y)
- || event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel;
+ final boolean onKeyguard =
+ mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
+
+ final boolean expand;
+ if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
+ // If we get a cancel, put the shade back to the state it was in when the gesture
+ // started
+ if (onKeyguard) {
+ expand = true;
+ } else {
+ expand = !mPanelClosedOnDown;
+ }
+ } else {
+ expand = flingExpands(vel, vectorVel, x, y);
+ }
+
mDozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
mStatusBar.isFalsingThresholdNeeded(), mStatusBar.isWakeUpComingFromTouch());
// Log collapse gesture if on lock screen.
- if (!expand && mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
+ if (!expand && onKeyguard) {
float displayDensity = mStatusBar.getDisplayDensity();
int heightDp = (int) Math.abs((y - mInitialTouchY) / displayDensity);
int velocityDp = (int) Math.abs(vel / displayDensity);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 60fc17d..686b871 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -348,10 +348,8 @@
}
if (mKeyguardUpdateMonitor.needsSlowUnlockTransition() && mState == ScrimState.UNLOCKED) {
- // In case the user isn't unlocked, make sure to delay a bit because the system is hosed
- // with too many things at this case, in order to not skip the initial frames.
- mScrimInFront.postOnAnimationDelayed(this::scheduleUpdate, 16);
mAnimationDelay = StatusBar.FADE_KEYGUARD_START_DELAY;
+ scheduleUpdate();
} else if ((!mDozeParameters.getAlwaysOn() && oldState == ScrimState.AOD)
|| (mState == ScrimState.AOD && !mDozeParameters.getDisplayNeedsBlanking())) {
// Scheduling a frame isn't enough when:
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/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 812ce1c..6dd96f92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -82,6 +82,7 @@
private boolean mClockVisibleByUser = true;
private boolean mAttached;
+ private boolean mScreenReceiverRegistered;
private Calendar mCalendar;
private String mClockFormatString;
private SimpleDateFormat mClockFormat;
@@ -213,6 +214,14 @@
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
+ if (mScreenReceiverRegistered) {
+ mScreenReceiverRegistered = false;
+ mBroadcastDispatcher.unregisterReceiver(mScreenReceiver);
+ if (mSecondsHandler != null) {
+ mSecondsHandler.removeCallbacks(mSecondTick);
+ mSecondsHandler = null;
+ }
+ }
if (mAttached) {
mBroadcastDispatcher.unregisterReceiver(mIntentReceiver);
mAttached = false;
@@ -363,12 +372,14 @@
mSecondsHandler.postAtTime(mSecondTick,
SystemClock.uptimeMillis() / 1000 * 1000 + 1000);
}
+ mScreenReceiverRegistered = true;
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
mBroadcastDispatcher.registerReceiver(mScreenReceiver, filter);
}
} else {
if (mSecondsHandler != null) {
+ mScreenReceiverRegistered = false;
mBroadcastDispatcher.unregisterReceiver(mScreenReceiver);
mSecondsHandler.removeCallbacks(mSecondTick);
mSecondsHandler = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 07de388..c43ad36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -106,9 +106,9 @@
public void updateNotification(@NonNull String key, boolean alert) {
super.updateNotification(key, alert);
- AlertEntry alertEntry = getHeadsUpEntry(key);
- if (alert && alertEntry != null) {
- setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(alertEntry.mEntry));
+ HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
+ if (alert && headsUpEntry != null) {
+ setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUpEntry.mEntry));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index 512d0f3..5ec5ec6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -24,7 +24,6 @@
import android.animation.ObjectAnimator;
import android.content.Context;
import android.database.DataSetObserver;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.util.AttributeSet;
@@ -36,6 +35,7 @@
import android.widget.FrameLayout;
import com.android.settingslib.animation.AppearAnimationUtils;
+import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -288,7 +288,8 @@
if (item.picture == null) {
v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId());
} else {
- Drawable drawable = new BitmapDrawable(v.getResources(), item.picture);
+ int avatarSize = (int) v.getResources().getDimension(R.dimen.kg_framed_avatar_size);
+ Drawable drawable = new CircleFramedDrawable(item.picture, avatarSize);
drawable.setColorFilter(
item.isSwitchToEnabled ? null : getDisabledUserAvatarColorFilter());
v.bind(name, drawable, item.info.id);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 3bd33cc..251693e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -16,11 +16,11 @@
package com.android.systemui.statusbar.policy;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+
import static com.android.settingslib.Utils.updateLocationEnabled;
import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -36,8 +36,9 @@
import androidx.annotation.VisibleForTesting;
import com.android.systemui.BootCompleteCache;
+import com.android.systemui.appops.AppOpItem;
+import com.android.systemui.appops.AppOpsController;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.util.Utils;
@@ -51,41 +52,32 @@
* A controller to manage changes of location related states and update the views accordingly.
*/
@Singleton
-public class LocationControllerImpl extends BroadcastReceiver implements LocationController {
+public class LocationControllerImpl extends BroadcastReceiver implements LocationController,
+ AppOpsController.Callback {
- private static final int[] mHighPowerRequestAppOpArray
- = new int[] {AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION};
+ private final Context mContext;
+ private final AppOpsController mAppOpsController;
+ private final BootCompleteCache mBootCompleteCache;
+ private final H mHandler;
- private Context mContext;
-
- private AppOpsManager mAppOpsManager;
- private StatusBarManager mStatusBarManager;
- private BroadcastDispatcher mBroadcastDispatcher;
- private BootCompleteCache mBootCompleteCache;
private boolean mAreActiveLocationRequests;
- private final H mHandler;
-
@Inject
- public LocationControllerImpl(Context context, @Main Looper mainLooper,
- @Background Looper bgLooper, BroadcastDispatcher broadcastDispatcher,
+ public LocationControllerImpl(Context context, AppOpsController appOpsController,
+ @Main Looper mainLooper, BroadcastDispatcher broadcastDispatcher,
BootCompleteCache bootCompleteCache) {
mContext = context;
- mBroadcastDispatcher = broadcastDispatcher;
+ mAppOpsController = appOpsController;
mBootCompleteCache = bootCompleteCache;
mHandler = new H(mainLooper);
// Register to listen for changes in location settings.
IntentFilter filter = new IntentFilter();
- filter.addAction(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
filter.addAction(LocationManager.MODE_CHANGED_ACTION);
- mBroadcastDispatcher.registerReceiverWithHandler(this, filter,
- new Handler(bgLooper), UserHandle.ALL);
+ broadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler, UserHandle.ALL);
- mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
- mStatusBarManager
- = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE);
+ mAppOpsController.addCallback(new int[]{OP_MONITOR_HIGH_POWER_LOCATION}, this);
// Examine the current location state and initialize the status view.
updateActiveLocationRequests();
@@ -160,27 +152,12 @@
*/
@VisibleForTesting
protected boolean areActiveHighPowerLocationRequests() {
- List<AppOpsManager.PackageOps> packages
- = mAppOpsManager.getPackagesForOps(mHighPowerRequestAppOpArray);
- // AppOpsManager can return null when there is no requested data.
- if (packages != null) {
- final int numPackages = packages.size();
- for (int packageInd = 0; packageInd < numPackages; packageInd++) {
- AppOpsManager.PackageOps packageOp = packages.get(packageInd);
- List<AppOpsManager.OpEntry> opEntries = packageOp.getOps();
- if (opEntries != null) {
- final int numOps = opEntries.size();
- for (int opInd = 0; opInd < numOps; opInd++) {
- AppOpsManager.OpEntry opEntry = opEntries.get(opInd);
- // AppOpsManager should only return OP_MONITOR_HIGH_POWER_LOCATION because
- // of the mHighPowerRequestAppOpArray filter, but checking defensively.
- if (opEntry.getOp() == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) {
- if (opEntry.isRunning()) {
- return true;
- }
- }
- }
- }
+ List<AppOpItem> appOpsItems = mAppOpsController.getActiveAppOps();
+
+ final int numItems = appOpsItems.size();
+ for (int i = 0; i < numItems; i++) {
+ if (appOpsItems.get(i).getCode() == OP_MONITOR_HIGH_POWER_LOCATION) {
+ return true;
}
}
@@ -198,14 +175,16 @@
@Override
public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)) {
- updateActiveLocationRequests();
- } else if (LocationManager.MODE_CHANGED_ACTION.equals(action)) {
- mHandler.sendEmptyMessage(H.MSG_LOCATION_SETTINGS_CHANGED);
+ if (LocationManager.MODE_CHANGED_ACTION.equals(intent.getAction())) {
+ mHandler.locationSettingsChanged();
}
}
+ @Override
+ public void onActiveStateChanged(int code, int uid, String packageName, boolean active) {
+ updateActiveLocationRequests();
+ }
+
private final class H extends Handler {
private static final int MSG_LOCATION_SETTINGS_CHANGED = 1;
private static final int MSG_LOCATION_ACTIVE_CHANGED = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/util/Assert.java b/packages/SystemUI/src/com/android/systemui/util/Assert.java
index 3f05657..e08936c 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Assert.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Assert.java
@@ -25,16 +25,21 @@
*/
public class Assert {
private static final Looper sMainLooper = Looper.getMainLooper();
- private static Looper sTestLooper = null;
+ private static Thread sTestThread = null;
@VisibleForTesting
public static void setTestableLooper(Looper testLooper) {
- sTestLooper = testLooper;
+ setTestThread(testLooper == null ? null : testLooper.getThread());
+ }
+
+ @VisibleForTesting
+ public static void setTestThread(Thread thread) {
+ sTestThread = thread;
}
public static void isMainThread() {
if (!sMainLooper.isCurrentThread()
- && (sTestLooper == null || !sTestLooper.isCurrentThread())) {
+ && (sTestThread == null || sTestThread != Thread.currentThread())) {
throw new IllegalStateException("should be called from the main thread."
+ " sMainLooper.threadName=" + sMainLooper.getThread().getName()
+ " Thread.currentThread()=" + Thread.currentThread().getName());
@@ -43,7 +48,7 @@
public static void isNotMainThread() {
if (sMainLooper.isCurrentThread()
- && (sTestLooper == null || sTestLooper.isCurrentThread())) {
+ && (sTestThread == null || sTestThread == Thread.currentThread())) {
throw new IllegalStateException("should not be called from the main thread.");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
index 7729965..bf22a98 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
@@ -32,6 +32,7 @@
import javax.inject.Singleton;
+import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -199,4 +200,10 @@
public static Executor provideUiBackgroundExecutor() {
return Executors.newSingleThreadExecutor();
}
+
+ /**
+ * Binds {@link ThreadFactoryImpl} to {@link ThreadFactory}.
+ */
+ @Binds
+ public abstract ThreadFactory bindExecutorFactory(ThreadFactoryImpl impl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java
new file mode 100644
index 0000000..0352fb5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java
@@ -0,0 +1,44 @@
+/*
+ * 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.util.concurrency;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Factory for building Executors running on a unique named thread.
+ *
+ * Use this when our generally available @Main, @Background, @UiBackground, @LongRunning, or
+ * similar global qualifiers don't quite cut it. Note that the methods here create entirely new
+ * threads; there are no singletons here. Use responsibly.
+ */
+public interface ThreadFactory {
+ /**
+ * Return an {@link java.util.concurrent.Executor} running on a named thread.
+ *
+ * The thread is implicitly started and may be left running indefinitely, depending on the
+ * implementation. Assume this is the case and use responsibly.
+ **/
+ Executor buildExecutorOnNewThread(String threadName);
+
+ /**
+ * Return an {@link DelayableExecutor} running on a named thread.
+ *
+ * The thread is implicitly started and may be left running indefinitely, depending on the
+ * implementation. Assume this is the case and use responsibly.
+ **/
+ DelayableExecutor buildDelayableExecutorOnNewThread(String threadName);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java
new file mode 100644
index 0000000..ca8d836
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java
@@ -0,0 +1,38 @@
+/*
+ * 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.util.concurrency;
+
+import android.os.HandlerThread;
+
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+class ThreadFactoryImpl implements ThreadFactory {
+ @Inject
+ ThreadFactoryImpl() {}
+
+ public Executor buildExecutorOnNewThread(String threadName) {
+ return buildDelayableExecutorOnNewThread(threadName);
+ }
+
+ public DelayableExecutor buildDelayableExecutorOnNewThread(String threadName) {
+ HandlerThread handlerThread = new HandlerThread(threadName);
+ handlerThread.start();
+ return new ExecutorImpl(handlerThread.getLooper());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
index 450336a..ed4df17 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
@@ -25,18 +25,18 @@
import android.hardware.SensorManager;
import android.hardware.TriggerEventListener;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.MemoryFile;
import android.util.Log;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.util.concurrency.ThreadFactory;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -56,25 +56,14 @@
private final SensorManager mInner;
private final List<Sensor> mSensorCache;
- private final Handler mHandler;
+ private final Executor mExecutor;
private final List<SensorManagerPlugin> mPlugins;
@Inject
- public AsyncSensorManager(SensorManager sensorManager, PluginManager pluginManager) {
- this(sensorManager, pluginManager, null);
- }
-
- @VisibleForTesting
- public AsyncSensorManager(
- SensorManager sensorManager, PluginManager pluginManager, Handler handler) {
+ public AsyncSensorManager(SensorManager sensorManager, ThreadFactory threadFactory,
+ PluginManager pluginManager) {
mInner = sensorManager;
- if (handler == null) {
- HandlerThread handlerThread = new HandlerThread("async_sensor");
- handlerThread.start();
- mHandler = new Handler(handlerThread.getLooper());
- } else {
- mHandler = handler;
- }
+ mExecutor = threadFactory.buildExecutorOnNewThread("async_sensor");
mSensorCache = mInner.getSensorList(Sensor.TYPE_ALL);
mPlugins = new ArrayList<>();
if (pluginManager != null) {
@@ -97,7 +86,7 @@
protected boolean registerListenerImpl(SensorEventListener listener,
Sensor sensor, int delayUs, Handler handler, int maxReportLatencyUs,
int reservedFlags) {
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
if (!mInner.registerListener(listener, sensor, delayUs, maxReportLatencyUs, handler)) {
Log.e(TAG, "Registering " + listener + " for " + sensor + " failed.");
}
@@ -129,12 +118,12 @@
@Override
protected void registerDynamicSensorCallbackImpl(DynamicSensorCallback callback,
Handler handler) {
- mHandler.post(() -> mInner.registerDynamicSensorCallback(callback, handler));
+ mExecutor.execute(() -> mInner.registerDynamicSensorCallback(callback, handler));
}
@Override
protected void unregisterDynamicSensorCallbackImpl(DynamicSensorCallback callback) {
- mHandler.post(() -> mInner.unregisterDynamicSensorCallback(callback));
+ mExecutor.execute(() -> mInner.unregisterDynamicSensorCallback(callback));
}
@Override
@@ -145,7 +134,7 @@
if (sensor == null) {
throw new IllegalArgumentException("sensor cannot be null");
}
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
if (!mInner.requestTriggerSensor(listener, sensor)) {
Log.e(TAG, "Requesting " + listener + " for " + sensor + " failed.");
}
@@ -158,7 +147,7 @@
boolean disable) {
Preconditions.checkArgument(disable);
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
if (!mInner.cancelTriggerSensor(listener, sensor)) {
Log.e(TAG, "Canceling " + listener + " for " + sensor + " failed.");
}
@@ -178,7 +167,7 @@
Log.w(TAG, "No plugins registered");
return false;
}
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
for (int i = 0; i < mPlugins.size(); i++) {
mPlugins.get(i).registerListener(sensor, listener);
}
@@ -194,7 +183,7 @@
*/
public void unregisterPluginListener(SensorManagerPlugin.Sensor sensor,
SensorManagerPlugin.SensorEventListener listener) {
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
for (int i = 0; i < mPlugins.size(); i++) {
mPlugins.get(i).unregisterListener(sensor, listener);
}
@@ -214,14 +203,14 @@
@Override
protected boolean setOperationParameterImpl(SensorAdditionalInfo parameter) {
- mHandler.post(() -> mInner.setOperationParameter(parameter));
+ mExecutor.execute(() -> mInner.setOperationParameter(parameter));
return true;
}
@Override
protected void unregisterListenerImpl(SensorEventListener listener,
Sensor sensor) {
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
if (sensor == null) {
mInner.unregisterListener(listener);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
index 8ba5b99..38b20c0 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -226,6 +226,8 @@
if (!activeControl.getSurfacePosition().equals(lastSurfacePosition)
&& mAnimation != null) {
startAnimation(mImeShowing, true /* forceRestart */);
+ } else if (!mImeShowing) {
+ removeImeSurface();
}
});
}
@@ -251,6 +253,11 @@
mHandler.post(() -> startAnimation(false /* show */, false /* forceRestart */));
}
+ @Override
+ public void topFocusedWindowChanged(String packageName) {
+ // no-op
+ }
+
/**
* Sends the local visibility state back to window manager. Needed for legacy adjustForIme.
*/
@@ -370,16 +377,7 @@
dispatchEndPositioning(mDisplayId, mCancelled, t);
if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) {
t.hide(mImeSourceControl.getLeash());
- final IInputMethodManager imms = getImms();
- if (imms != null) {
- try {
- // Remove the IME surface to make the insets invisible for
- // non-client controlled insets.
- imms.removeImeSurface();
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to remove IME surface.", e);
- }
- }
+ removeImeSurface();
}
t.apply();
mTransactionPool.release(t);
@@ -402,6 +400,19 @@
}
}
+ void removeImeSurface() {
+ final IInputMethodManager imms = getImms();
+ if (imms != null) {
+ try {
+ // Remove the IME surface to make the insets invisible for
+ // non-client controlled insets.
+ imms.removeImeSurface();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to remove IME surface.", e);
+ }
+ }
+ }
+
/**
* Allows other things to synchronize with the ime position
*/
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/SysuiTestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
index f29f042..3d679de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
@@ -26,11 +26,14 @@
import android.util.Log;
import android.view.Display;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.Set;
public class SysuiTestableContext extends TestableContext {
- private Set<BroadcastReceiver> mRegisteredReceivers = new ArraySet<>();
+ @GuardedBy("mRegisteredReceivers")
+ private final Set<BroadcastReceiver> mRegisteredReceivers = new ArraySet<>();
public SysuiTestableContext(Context base) {
super(base);
@@ -54,7 +57,11 @@
}
public void cleanUpReceivers(String testName) {
- Set<BroadcastReceiver> copy = new ArraySet<>(mRegisteredReceivers);
+ Set<BroadcastReceiver> copy;
+ synchronized (mRegisteredReceivers) {
+ copy = new ArraySet<>(mRegisteredReceivers);
+ mRegisteredReceivers.clear();
+ }
for (BroadcastReceiver r : copy) {
try {
unregisterReceiver(r);
@@ -68,7 +75,9 @@
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
if (receiver != null) {
- mRegisteredReceivers.add(receiver);
+ synchronized (mRegisteredReceivers) {
+ mRegisteredReceivers.add(receiver);
+ }
}
return super.registerReceiver(receiver, filter);
}
@@ -77,7 +86,9 @@
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
if (receiver != null) {
- mRegisteredReceivers.add(receiver);
+ synchronized (mRegisteredReceivers) {
+ mRegisteredReceivers.add(receiver);
+ }
}
return super.registerReceiver(receiver, filter, broadcastPermission, scheduler);
}
@@ -86,7 +97,9 @@
public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
IntentFilter filter, String broadcastPermission, Handler scheduler) {
if (receiver != null) {
- mRegisteredReceivers.add(receiver);
+ synchronized (mRegisteredReceivers) {
+ mRegisteredReceivers.add(receiver);
+ }
}
return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission, scheduler);
}
@@ -94,7 +107,9 @@
@Override
public void unregisterReceiver(BroadcastReceiver receiver) {
if (receiver != null) {
- mRegisteredReceivers.remove(receiver);
+ synchronized (mRegisteredReceivers) {
+ mRegisteredReceivers.remove(receiver);
+ }
}
super.unregisterReceiver(receiver);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 5b46b7f..15828b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -63,6 +63,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.QuickStepContract;
@@ -135,6 +136,8 @@
@Mock
private SysuiStatusBarStateController mStatusBarStateController;
@Mock
+ private KeyguardViewMediator mKeyguardViewMediator;
+ @Mock
private KeyguardBypassController mKeyguardBypassController;
@Mock
private FloatingContentCoordinator mFloatingContentCoordinator;
@@ -198,8 +201,8 @@
mNotificationShadeWindowController = new NotificationShadeWindowController(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
- mConfigurationController, mKeyguardBypassController, mColorExtractor,
- mDumpManager);
+ mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
+ mColorExtractor, mDumpManager);
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowController.attach();
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..f7f3a37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
@@ -35,10 +35,10 @@
import androidx.test.filters.SmallTest;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.tests.R;
import org.junit.Before;
import org.junit.Test;
@@ -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/bubbles/BubblesTestActivity.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java
index 8bc2e2b..43d2ad1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java
@@ -20,7 +20,7 @@
import android.content.Intent;
import android.os.Bundle;
-import com.android.systemui.tests.R;
+import com.android.systemui.R;
/**
* Referenced by NotificationTestHelper#makeBubbleMetadata
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 058478f..686a094 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -60,6 +60,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
@@ -132,6 +133,8 @@
@Mock
private SysuiStatusBarStateController mStatusBarStateController;
@Mock
+ private KeyguardViewMediator mKeyguardViewMediator;
+ @Mock
private KeyguardBypassController mKeyguardBypassController;
@Mock
private FloatingContentCoordinator mFloatingContentCoordinator;
@@ -194,8 +197,8 @@
// Bubbles get added to status bar window view
mNotificationShadeWindowController = new NotificationShadeWindowController(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
- mConfigurationController, mKeyguardBypassController, mColorExtractor,
- mDumpManager);
+ mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
+ mColorExtractor, mDumpManager);
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowController.attach();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
index 00d333fb..c591c1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
@@ -16,13 +16,13 @@
package com.android.systemui.doze;
-import android.hardware.display.AmbientDisplayConfiguration;
-
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
+import android.hardware.display.AmbientDisplayConfiguration;
+
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.sensors.FakeSensorManager;
@@ -37,7 +37,6 @@
when(params.getPulseOnSigMotion()).thenReturn(false);
when(params.getPickupVibrationThreshold()).thenReturn(0);
when(params.getProxCheckBeforePulse()).thenReturn(true);
- when(params.getPolicy()).thenReturn(mock(AlwaysOnDisplayPolicy.class));
when(params.doubleTapReportsTouchCoordinates()).thenReturn(false);
when(params.getDisplayNeedsBlanking()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
index dc02799..5c2b153 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
@@ -57,7 +57,8 @@
MockitoAnnotations.initMocks(this);
mConfig = DozeConfigurationUtil.createMockConfig();
mDockManagerFake = spy(new DockManagerFake());
- mDockHandler = new DozeDockHandler(mConfig, mMachine, mDockManagerFake);
+ mDockHandler = new DozeDockHandler(mConfig, mDockManagerFake);
+ mDockHandler.setDozeMachine(mMachine);
when(mMachine.getState()).thenReturn(State.DOZE_AOD);
doReturn(true).when(mConfig).alwaysOnEnabled(anyInt());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index 1f07f46..8078b6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -88,8 +88,7 @@
mMachine = new DozeMachine(mServiceFake, mConfigMock, mWakeLockFake,
mWakefulnessLifecycle, mock(BatteryController.class), mDozeLog, mDockManager,
- mHost);
- mMachine.setParts(new DozeMachine.Part[]{mPartMock});
+ mHost, new DozeMachine.Part[]{mPartMock});
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 3ef6027..3e60e016 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -40,14 +40,17 @@
import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
import android.view.Display;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.concurrency.FakeThreadFactory;
+import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.FakeSensorManager;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -55,22 +58,24 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidJUnit4.class)
@SmallTest
+@RunWith(AndroidTestingRunner.class)
public class DozeScreenBrightnessTest extends SysuiTestCase {
- static final int DEFAULT_BRIGHTNESS = 10;
- static final int[] SENSOR_TO_BRIGHTNESS = new int[]{-1, 1, 2, 3, 4};
- static final int[] SENSOR_TO_OPACITY = new int[]{-1, 10, 0, 0, 0};
+ private static final int DEFAULT_BRIGHTNESS = 10;
+ private static final int[] SENSOR_TO_BRIGHTNESS = new int[]{-1, 1, 2, 3, 4};
+ private static final int[] SENSOR_TO_OPACITY = new int[]{-1, 10, 0, 0, 0};
- DozeServiceFake mServiceFake;
- FakeSensorManager.FakeGenericSensor mSensor;
- FakeSensorManager mSensorManager;
+ private DozeServiceFake mServiceFake;
+ private FakeSensorManager.FakeGenericSensor mSensor;
+ private AsyncSensorManager mSensorManager;
+ private AlwaysOnDisplayPolicy mAlwaysOnDisplayPolicy;
@Mock
DozeHost mDozeHost;
- @Mock
- BroadcastDispatcher mBroadcastDispatcher;
- DozeScreenBrightness mScreen;
+ private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+ private FakeThreadFactory mFakeThreadFactory = new FakeThreadFactory(mFakeExecutor);
+
+ private DozeScreenBrightness mScreen;
@Before
public void setUp() throws Exception {
@@ -83,12 +88,17 @@
return null;
}).when(mDozeHost).prepareForGentleSleep(any());
mServiceFake = new DozeServiceFake();
- mSensorManager = new FakeSensorManager(mContext);
- mSensor = mSensorManager.getFakeLightSensor();
+ FakeSensorManager fakeSensorManager = new FakeSensorManager(mContext);
+ mSensorManager = new AsyncSensorManager(fakeSensorManager, mFakeThreadFactory, null);
+
+ mAlwaysOnDisplayPolicy = new AlwaysOnDisplayPolicy(mContext);
+ mAlwaysOnDisplayPolicy.defaultDozeBrightness = DEFAULT_BRIGHTNESS;
+ mAlwaysOnDisplayPolicy.screenBrightnessArray = SENSOR_TO_BRIGHTNESS;
+ mAlwaysOnDisplayPolicy.dimmingScrimArray = SENSOR_TO_OPACITY;
+ mSensor = fakeSensorManager.getFakeLightSensor();
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
- mSensor.getSensor(), mBroadcastDispatcher, mDozeHost, null /* handler */,
- DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY,
- true /* debuggable */);
+ mSensor.getSensor(), mDozeHost, null /* handler */,
+ mAlwaysOnDisplayPolicy);
mScreen.onScreenState(Display.STATE_ON);
}
@@ -106,6 +116,7 @@
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.onScreenState(Display.STATE_DOZE);
+ waitForSensorManager();
mSensor.sendSensorEvent(3);
@@ -116,6 +127,8 @@
public void testAod_usesDebugValue() throws Exception {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+ mScreen.onScreenState(Display.STATE_DOZE);
+ waitForSensorManager();
Intent intent = new Intent(DozeScreenBrightness.ACTION_AOD_BRIGHTNESS);
intent.putExtra(DozeScreenBrightness.BRIGHTNESS_BUCKET, 1);
@@ -141,6 +154,7 @@
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.onScreenState(Display.STATE_DOZE);
+ waitForSensorManager();
mSensor.sendSensorEvent(1);
@@ -153,9 +167,8 @@
@Test
public void testPulsing_withoutLightSensor_setsAoDDimmingScrimTransparent() throws Exception {
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
- null /* sensor */, mBroadcastDispatcher, mDozeHost, null /* handler */,
- DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY,
- true /* debuggable */);
+ null /* sensor */, mDozeHost, null /* handler */,
+ mAlwaysOnDisplayPolicy);
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE);
reset(mDozeHost);
@@ -174,6 +187,7 @@
mScreen.transitionTo(DOZE_PULSING, DOZE_PULSE_DONE);
mScreen.transitionTo(DOZE_PULSE_DONE, DOZE);
mScreen.onScreenState(Display.STATE_DOZE);
+ waitForSensorManager();
mSensor.sendSensorEvent(1);
@@ -183,9 +197,8 @@
@Test
public void testNullSensor() throws Exception {
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
- null /* sensor */, mBroadcastDispatcher, mDozeHost, null /* handler */,
- DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY,
- true /* debuggable */);
+ null /* sensor */, mDozeHost, null /* handler */,
+ mAlwaysOnDisplayPolicy);
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
@@ -198,6 +211,7 @@
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.transitionTo(DOZE_AOD, FINISH);
+ waitForSensorManager();
mSensor.sendSensorEvent(1);
@@ -205,10 +219,11 @@
}
@Test
- public void testNonPositiveBrightness_keepsPreviousBrightnessAndScrim() throws Exception {
+ public void testNonPositiveBrightness_keepsPreviousBrightnessAndScrim() {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.onScreenState(Display.STATE_DOZE);
+ waitForSensorManager();
mSensor.sendSensorEvent(1);
mSensor.sendSensorEvent(0);
@@ -222,6 +237,7 @@
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.onScreenState(Display.STATE_DOZE);
+ waitForSensorManager();
mSensor.sendSensorEvent(2);
@@ -233,6 +249,7 @@
reset(mDozeHost);
mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
mScreen.onScreenState(Display.STATE_DOZE);
+ waitForSensorManager();
mSensor.sendSensorEvent(2);
verify(mDozeHost).setAodDimmingScrim(eq(0f));
}
@@ -242,6 +259,7 @@
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.onScreenState(Display.STATE_DOZE);
+ waitForSensorManager();
mSensor.sendSensorEvent(2);
mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
@@ -251,4 +269,8 @@
mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
verify(mDozeHost).setAodDimmingScrim(eq(0f));
}
+
+ private void waitForSensorManager() {
+ mFakeExecutor.runAllReady();
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index ebd2c3a..7ebead8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -152,7 +152,7 @@
private class TestableDozeSensors extends DozeSensors {
TestableDozeSensors() {
- super(getContext(), mAlarmManager, mSensorManager, mDozeParameters,
+ super(getContext(), mSensorManager, mDozeParameters,
mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
mProximitySensor);
for (TriggerSensor sensor : mSensors) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 655f933..d3af835 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -30,9 +30,7 @@
import android.app.AlarmManager;
import android.hardware.Sensor;
import android.hardware.display.AmbientDisplayConfiguration;
-import android.os.Handler;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.Display;
@@ -43,6 +41,7 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.concurrency.FakeThreadFactory;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.FakeProximitySensor;
import com.android.systemui.util.sensors.FakeSensorManager;
@@ -92,15 +91,16 @@
mTapSensor = mSensors.getFakeTapSensor().getSensor();
WakeLock wakeLock = new WakeLockFake();
AsyncSensorManager asyncSensorManager =
- new AsyncSensorManager(mSensors, null, new Handler());
+ new AsyncSensorManager(mSensors, new FakeThreadFactory(mExecutor), null);
FakeThresholdSensor thresholdSensor = new FakeThresholdSensor();
thresholdSensor.setLoaded(true);
mProximitySensor = new FakeProximitySensor(thresholdSensor, null, mExecutor);
- mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, config, parameters,
- asyncSensorManager, wakeLock, true, mDockManager, mProximitySensor,
+ mTriggers = new DozeTriggers(mContext, mHost, mAlarmManager, config, parameters,
+ asyncSensorManager, wakeLock, mDockManager, mProximitySensor,
mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher);
+ mTriggers.setDozeMachine(mMachine);
waitForSensorManager();
}
@@ -186,6 +186,6 @@
}
private void waitForSensorManager() {
- TestableLooper.get(this).processAllMessages();
+ mExecutor.runAllReady();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
index c5bddc1..069699c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
@@ -81,8 +81,9 @@
mWakeLock = new WakeLockFake();
mHandler = mHandlerThread.getThreadHandler();
- mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler,
+ mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
mDozeParameters, mKeyguardUpdateMonitor, mDozeLog);
+ mDozeUi.setDozeMachine(mMachine);
}
@After
@@ -136,8 +137,9 @@
reset(mDozeParameters);
reset(mHost);
when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
- mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler,
+ mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
mDozeParameters, mKeyguardUpdateMonitor, mDozeLog);
+ mDozeUi.setDozeMachine(mMachine);
// Never animate if display doesn't support it.
mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
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..492b33e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
@@ -22,6 +22,7 @@
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import android.graphics.Color;
@@ -39,6 +40,7 @@
import org.mockito.Mock;
import java.util.ArrayList;
+import java.util.Map;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -46,6 +48,7 @@
public class MediaDataCombineLatestTest extends SysuiTestCase {
private static final String KEY = "TEST_KEY";
+ private static final String OLD_KEY = "TEST_KEY_OLD";
private static final String APP = "APP";
private static final String PACKAGE = "PKG";
private static final int BG_COLOR = Color.RED;
@@ -96,7 +99,7 @@
@Test
public void eventNotEmittedWithoutMedia() {
// WHEN device source emits an event without media data
- mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
+ mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
// THEN an event isn't emitted
verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any());
}
@@ -104,7 +107,7 @@
@Test
public void emitEventAfterDeviceFirst() {
// GIVEN that a device event has already been received
- mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
+ mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
// WHEN media event is received
mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
// THEN the listener receives a combined event
@@ -118,7 +121,7 @@
// GIVEN that media event has already been received
mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
// WHEN device event is received
- mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
+ mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
// THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture());
@@ -126,6 +129,64 @@
}
@Test
+ public void migrateKeyMediaFirst() {
+ // GIVEN that media and device info has already been received
+ mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData);
+ mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
+ reset(mListener);
+ // WHEN a key migration event is received
+ mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData);
+ // THEN the listener receives a combined event
+ ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
+ verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture());
+ assertThat(captor.getValue().getDevice()).isNotNull();
+ }
+
+ @Test
+ public void migrateKeyDeviceFirst() {
+ // GIVEN that media and device info has already been received
+ mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData);
+ mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
+ reset(mListener);
+ // WHEN a key migration event is received
+ mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
+ // THEN the listener receives a combined event
+ ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
+ verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture());
+ assertThat(captor.getValue().getDevice()).isNotNull();
+ }
+
+ @Test
+ public void migrateKeyMediaAfter() {
+ // GIVEN that media and device info has already been received
+ mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData);
+ mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
+ mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
+ reset(mListener);
+ // WHEN a second key migration event is received for media
+ mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData);
+ // THEN the key has already been migrated
+ ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
+ verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture());
+ assertThat(captor.getValue().getDevice()).isNotNull();
+ }
+
+ @Test
+ public void migrateKeyDeviceAfter() {
+ // GIVEN that media and device info has already been received
+ mDataListener.onMediaDataLoaded(OLD_KEY, null, mMediaData);
+ mDeviceListener.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
+ mDataListener.onMediaDataLoaded(KEY, OLD_KEY, mMediaData);
+ reset(mListener);
+ // WHEN a second key migration event is received for the device
+ mDeviceListener.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
+ // THEN the key has already be migrated
+ ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
+ verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture());
+ assertThat(captor.getValue().getDevice()).isNotNull();
+ }
+
+ @Test
public void mediaDataRemoved() {
// WHEN media data is removed without first receiving device or data
mDataListener.onMediaDataRemoved(KEY);
@@ -142,7 +203,7 @@
@Test
public void mediaDataRemovedAfterDeviceEvent() {
- mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
+ mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
mDataListener.onMediaDataRemoved(KEY);
verify(mListener).onMediaDataRemoved(eq(KEY));
}
@@ -151,7 +212,7 @@
public void mediaDataKeyUpdated() {
// GIVEN that device and media events have already been received
mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
- mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
+ mDeviceListener.onMediaDeviceChanged(KEY, null, mDeviceData);
// WHEN the key is changed
mDataListener.onMediaDataLoaded("NEW_KEY", KEY, mMediaData);
// THEN the listener gets a load event with the correct keys
@@ -159,6 +220,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, null, 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/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 6761b28..59c2d0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -31,6 +31,7 @@
import org.mockito.Mockito.`when` as whenever
private const val KEY = "KEY"
+private const val KEY_2 = "KEY_2"
private const val PACKAGE_NAME = "com.android.systemui"
private const val APP_NAME = "SystemUI"
private const val SESSION_ARTIST = "artist"
@@ -156,8 +157,43 @@
mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
// WHEN the notification is removed
mediaDataManager.onNotificationRemoved(KEY)
- // THEN the media data indicates that it is
+ // THEN the media data indicates that it is for resumption
assertThat(listener.data!!.resumption).isTrue()
+ // AND the new key is the package name
+ assertThat(listener.key!!).isEqualTo(PACKAGE_NAME)
+ assertThat(listener.oldKey!!).isEqualTo(KEY)
+ assertThat(listener.removedKey).isNull()
+ }
+
+ @Test
+ fun testOnNotificationRemoved_twoWithResumption() {
+ // GIVEN that the manager has two notifications with resume actions
+ val listener = TestListener()
+ mediaDataManager.addListener(listener)
+ whenever(controller.metadata).thenReturn(metadataBuilder.build())
+ mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+ mediaDataManager.onNotificationAdded(KEY_2, mediaNotification)
+ assertThat(backgroundExecutor.runAllReady()).isEqualTo(2)
+ assertThat(foregroundExecutor.runAllReady()).isEqualTo(2)
+ val data = listener.data!!
+ assertThat(data.resumption).isFalse()
+ val resumableData = data.copy(resumeAction = Runnable {})
+ mediaDataManager.onMediaDataLoaded(KEY, null, resumableData)
+ mediaDataManager.onMediaDataLoaded(KEY_2, null, resumableData)
+ // WHEN the first is removed
+ mediaDataManager.onNotificationRemoved(KEY)
+ // THEN the data is for resumption and the key is migrated to the package name
+ assertThat(listener.data!!.resumption).isTrue()
+ assertThat(listener.key!!).isEqualTo(PACKAGE_NAME)
+ assertThat(listener.oldKey!!).isEqualTo(KEY)
+ assertThat(listener.removedKey).isNull()
+ // WHEN the second is removed
+ mediaDataManager.onNotificationRemoved(KEY_2)
+ // THEN the data is for resumption and the second key is removed
+ assertThat(listener.data!!.resumption).isTrue()
+ assertThat(listener.key!!).isEqualTo(PACKAGE_NAME)
+ assertThat(listener.oldKey!!).isEqualTo(PACKAGE_NAME)
+ assertThat(listener.removedKey!!).isEqualTo(KEY_2)
}
@Test
@@ -190,6 +226,7 @@
var data: MediaData? = null
var key: String? = null
var oldKey: String? = null
+ var removedKey: String? = null
override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
this.key = key
@@ -198,9 +235,7 @@
}
override fun onMediaDataRemoved(key: String) {
- this.key = key
- oldKey = null
- data = null
+ removedKey = key
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
index fc22eeb..3c6e19f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -166,7 +166,7 @@
// THEN the listener for the old key should removed.
verify(lmm).unregisterCallback(any())
// AND a new device event emitted
- val data = captureDeviceData(KEY)
+ val data = captureDeviceData(KEY, KEY_OLD)
assertThat(data.enabled).isTrue()
assertThat(data.name).isEqualTo(DEVICE_NAME)
}
@@ -179,13 +179,14 @@
// WHEN the new key is the same as the old key
manager.onMediaDataLoaded(KEY, KEY, mediaData)
// THEN no event should be emitted
- verify(listener, never()).onMediaDeviceChanged(eq(KEY), any())
+ verify(listener, never()).onMediaDeviceChanged(eq(KEY), eq(null), any())
}
@Test
fun unknownOldKey() {
- manager.onMediaDataLoaded(KEY, "unknown", mediaData)
- verify(listener).onMediaDeviceChanged(eq(KEY), any())
+ val oldKey = "unknown"
+ manager.onMediaDataLoaded(KEY, oldKey, mediaData)
+ verify(listener).onMediaDeviceChanged(eq(KEY), eq(oldKey), any())
}
@Test
@@ -223,7 +224,7 @@
manager.removeListener(listener)
// THEN it doesn't receive device events
manager.onMediaDataLoaded(KEY, null, mediaData)
- verify(listener, never()).onMediaDeviceChanged(eq(KEY), any())
+ verify(listener, never()).onMediaDeviceChanged(eq(KEY), eq(null), any())
}
@Test
@@ -318,9 +319,9 @@
return captor.getValue()
}
- fun captureDeviceData(key: String): MediaDeviceData {
+ fun captureDeviceData(key: String, oldKey: String? = null): MediaDeviceData {
val captor = ArgumentCaptor.forClass(MediaDeviceData::class.java)
- verify(listener).onMediaDeviceChanged(eq(key), captor.capture())
+ verify(listener).onMediaDeviceChanged(eq(key), eq(oldKey), captor.capture())
return captor.getValue()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java
index 2d9fa1b..583d069 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java
@@ -16,7 +16,7 @@
package com.android.systemui.onehanded;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
@@ -52,12 +52,12 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mOneHandedAnimationController = new OneHandedAnimationController(mContext,
+ mOneHandedAnimationController = new OneHandedAnimationController(
new OneHandedSurfaceTransactionHelper(mContext));
}
@Test
- public void testGetAnimator_withSameBounds_returnTranslateAnimator() {
+ public void testGetAnimator_withSameBounds_returnAnimator() {
final Rect originalBounds = new Rect(0, 0, TEST_BOUNDS_WIDTH, TEST_BOUNDS_HEIGHT);
final Rect destinationBounds = originalBounds;
destinationBounds.offset(0, 300);
@@ -65,7 +65,6 @@
mOneHandedAnimationController
.getAnimator(mMockLeash, originalBounds, destinationBounds);
- assertEquals("Expected translate animation",
- OneHandedAnimationController.ANIM_TYPE_TRANSLATE, animator.getAnimationType());
+ assertNotNull(animator);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
index 28dad14..07a32a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -29,11 +29,11 @@
import static org.mockito.Mockito.when;
import android.content.res.Configuration;
-import android.graphics.Rect;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.view.DisplayInfo;
+import android.view.Display;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.window.DisplayAreaInfo;
import android.window.IWindowContainerToken;
@@ -58,7 +58,7 @@
static final int DISPLAY_HEIGHT = 1000;
DisplayAreaInfo mDisplayAreaInfo;
- DisplayInfo mDisplayInfo;
+ Display mDisplay;
Handler mUpdateHandler;
OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
OneHandedAnimationController.OneHandedTransitionAnimator mFakeAnimator;
@@ -80,20 +80,13 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(mContext,
- mMockDisplayController,
- mMockAnimationController,
- mMockSurfaceTransactionHelper);
- mUpdateHandler = Mockito.spy(mDisplayAreaOrganizer.getUpdateHandler());
mToken = new WindowContainerToken(mMockRealToken);
mLeash = new SurfaceControl();
+ mDisplay = mContext.getDisplay();
mDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY, FEATURE_ONE_HANDED);
mDisplayAreaInfo.configuration.orientation = Configuration.ORIENTATION_PORTRAIT;
- mDisplayInfo = new DisplayInfo();
- mDisplayInfo.logicalWidth = DISPLAY_WIDTH;
- mDisplayInfo.logicalHeight = DISPLAY_HEIGHT;
-
when(mMockAnimationController.getAnimator(any(), any(), any())).thenReturn(null);
+ when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
when(mMockSurfaceTransactionHelper.translate(any(), any(), anyFloat())).thenReturn(
mMockSurfaceTransactionHelper);
when(mMockSurfaceTransactionHelper.crop(any(), any(), any())).thenReturn(
@@ -106,8 +99,11 @@
when(mMockAnimator.setTransitionDirection(anyInt())).thenReturn(mFakeAnimator);
when(mMockLeash.getWidth()).thenReturn(DISPLAY_WIDTH);
when(mMockLeash.getHeight()).thenReturn(DISPLAY_HEIGHT);
- when(mMockDisplayController.getDisplay(anyInt())).thenReturn(null);
+ mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(mContext,
+ mMockDisplayController,
+ mMockAnimationController);
+ mUpdateHandler = Mockito.spy(mDisplayAreaOrganizer.getUpdateHandler());
}
@Test
@@ -135,7 +131,7 @@
mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
mDisplayAreaOrganizer.onDisplayAreaInfoChanged(newDisplayAreaInfo);
- assertThat(mDisplayAreaOrganizer.mDisplayAreaInfo).isEqualTo(newDisplayAreaInfo);
+ assertThat(mDisplayAreaOrganizer.mDisplayAreaMap.containsKey(mDisplayAreaInfo)).isTrue();
}
@Test
@@ -154,13 +150,7 @@
public void testResetImmediately() {
// To prevent mNativeObject of Surface is null in the test flow
when(mMockLeash.isValid()).thenReturn(false);
- final Rect newBounds = new Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
- final DisplayAreaInfo newDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY,
- FEATURE_ONE_HANDED);
- newDisplayAreaInfo.configuration.orientation = Configuration.ORIENTATION_LANDSCAPE;
-
- mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mMockLeash);
- mDisplayAreaOrganizer.onDisplayAreaInfoChanged(newDisplayAreaInfo);
+ mDisplayAreaOrganizer.onRotateDisplay(mContext.getResources(), Surface.ROTATION_90);
assertThat(mUpdateHandler.hasMessages(
OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isNotNull();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
index f4fc7ea..b6b2217 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
@@ -28,6 +28,7 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.Display;
import androidx.test.filters.SmallTest;
@@ -45,6 +46,7 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class OneHandedManagerImplTest extends OneHandedTestCase {
+ Display mDisplay;
OneHandedManagerImpl mOneHandedManagerImpl;
OneHandedTimeoutHandler mTimeoutHandler;
@@ -53,8 +55,6 @@
@Mock
OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
@Mock
- OneHandedSurfaceTransactionHelper mMockSurfaceTransactionHelper;
- @Mock
OneHandedTouchHandler mMockTouchHandler;
@Mock
OneHandedGestureHandler mMockGestureHandler;
@@ -64,6 +64,7 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mDisplay = mContext.getDisplay();
mOneHandedManagerImpl = new OneHandedManagerImpl(getContext(),
mMockDisplayController,
mMockDisplayAreaOrganizer,
@@ -72,19 +73,18 @@
mMockSysUiState);
mTimeoutHandler = Mockito.spy(OneHandedTimeoutHandler.get());
+ when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
}
-
@Test
public void testDefaultShouldNotInOneHanded() {
final OneHandedSurfaceTransactionHelper transactionHelper =
new OneHandedSurfaceTransactionHelper(mContext);
final OneHandedAnimationController animationController = new OneHandedAnimationController(
- mContext, transactionHelper);
+ transactionHelper);
OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer(
- mContext, mMockDisplayController, animationController,
- mMockSurfaceTransactionHelper);
+ mContext, mMockDisplayController, animationController);
assertThat(displayAreaOrganizer.isInOneHanded()).isFalse();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchStateTest.java
index 3155e57..17b2e32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchStateTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.pip.phone;
+import static android.view.MotionEvent.ACTION_BUTTON_PRESS;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
@@ -49,13 +50,17 @@
private PipTouchState mTouchState;
private CountDownLatch mDoubleTapCallbackTriggeredLatch;
+ private CountDownLatch mHoverExitCallbackTriggeredLatch;
@Before
public void setUp() throws Exception {
mDoubleTapCallbackTriggeredLatch = new CountDownLatch(1);
+ mHoverExitCallbackTriggeredLatch = new CountDownLatch(1);
mTouchState = new PipTouchState(ViewConfiguration.get(getContext()),
Handler.createAsync(Looper.myLooper()), () -> {
mDoubleTapCallbackTriggeredLatch.countDown();
+ }, () -> {
+ mHoverExitCallbackTriggeredLatch.countDown();
});
assertFalse(mTouchState.isDoubleTap());
assertFalse(mTouchState.isWaitingForDoubleTap());
@@ -120,6 +125,35 @@
assertTrue(mTouchState.getDoubleTapTimeoutCallbackDelay() == -1);
}
+ @Test
+ public void testHoverExitTimeout_timeoutCallbackCalled() throws Exception {
+ mTouchState.scheduleHoverExitTimeoutCallback();
+
+ // TODO: Remove this sleep. Its only being added because it speeds up this test a bit.
+ Thread.sleep(50);
+ TestableLooper.get(this).processAllMessages();
+ assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 0);
+ }
+
+ @Test
+ public void testHoverExitTimeout_timeoutCallbackNotCalled() throws Exception {
+ mTouchState.scheduleHoverExitTimeoutCallback();
+ TestableLooper.get(this).processAllMessages();
+ assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 1);
+ }
+
+ @Test
+ public void testHoverExitTimeout_timeoutCallbackNotCalled_ifButtonPress() throws Exception {
+ mTouchState.scheduleHoverExitTimeoutCallback();
+ mTouchState.onTouchEvent(createMotionEvent(ACTION_BUTTON_PRESS, SystemClock.uptimeMillis(),
+ 0, 0));
+
+ // TODO: Remove this sleep. Its only being added because it speeds up this test a bit.
+ Thread.sleep(50);
+ TestableLooper.get(this).processAllMessages();
+ assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 1);
+ }
+
private MotionEvent createMotionEvent(int action, long eventTime, float x, float y) {
return MotionEvent.obtain(0, eventTime, action, x, y, 0);
}
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/notification/MediaNotificationProcessorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
index e6287e7..7eeae67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
@@ -38,8 +38,8 @@
import androidx.palette.graphics.Palette;
import androidx.test.runner.AndroidJUnit4;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.tests.R;
import org.junit.After;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 25da741..a2f8c1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -50,7 +50,9 @@
import androidx.test.filters.SmallTest;
import androidx.test.filters.Suppress;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
@@ -59,7 +61,6 @@
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.policy.SmartReplyConstants;
-import com.android.systemui.tests.R;
import org.junit.Assert;
import org.junit.Before;
@@ -110,6 +111,7 @@
() -> smartReplyConstants,
() -> smartReplyController,
mConversationNotificationProcessor,
+ mock(MediaFeatureFlag.class),
mock(Executor.class));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index 5640f082..c5374b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -43,6 +43,7 @@
import com.android.internal.util.NotificationMessagingUtil;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.plugins.PluginManager;
@@ -86,6 +87,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
@@ -120,8 +122,8 @@
@Mock private NotificationGutsManager mGutsManager;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private NotificationMediaManager mNotificationMediaManager;
- @Mock private ExpandableNotificationRowComponent.Builder
- mExpandableNotificationRowComponentBuilder;
+ @Mock(answer = Answers.RETURNS_SELF)
+ private ExpandableNotificationRowComponent.Builder mExpandableNotificationRowComponentBuilder;
@Mock private ExpandableNotificationRowComponent mExpandableNotificationRowComponent;
@Mock private FalsingManager mFalsingManager;
@Mock private KeyguardBypassController mKeyguardBypassController;
@@ -197,6 +199,7 @@
() -> mock(SmartReplyConstants.class),
() -> mock(SmartReplyController.class),
mock(ConversationNotificationProcessor.class),
+ mock(MediaFeatureFlag.class),
mBgExecutor);
mRowContentBindStage = new RowContentBindStage(
binder,
@@ -209,21 +212,9 @@
when(mExpandableNotificationRowComponentBuilder
.expandableNotificationRow(viewCaptor.capture()))
.thenReturn(mExpandableNotificationRowComponentBuilder);
- when(mExpandableNotificationRowComponentBuilder
- .notificationEntry(any()))
- .thenReturn(mExpandableNotificationRowComponentBuilder);
- when(mExpandableNotificationRowComponentBuilder
- .onDismissRunnable(any()))
- .thenReturn(mExpandableNotificationRowComponentBuilder);
- when(mExpandableNotificationRowComponentBuilder
- .rowContentBindStage(any()))
- .thenReturn(mExpandableNotificationRowComponentBuilder);
- when(mExpandableNotificationRowComponentBuilder
- .onExpandClickListener(any()))
- .thenReturn(mExpandableNotificationRowComponentBuilder);
-
when(mExpandableNotificationRowComponentBuilder.build())
.thenReturn(mExpandableNotificationRowComponent);
+
when(mExpandableNotificationRowComponent.getExpandableNotificationRowController())
.thenAnswer((Answer<ExpandableNotificationRowController>) invocation ->
new ExpandableNotificationRowController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 362914d..8ccbb2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -43,9 +43,11 @@
import android.view.LayoutInflater;
import android.widget.RemoteViews;
+import com.android.systemui.R;
import com.android.systemui.TestableDependency;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.bubbles.BubblesTestActivity;
+import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -68,7 +70,6 @@
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.SmartReplyConstants;
-import com.android.systemui.tests.R;
import org.mockito.ArgumentCaptor;
@@ -133,6 +134,7 @@
() -> mock(SmartReplyConstants.class),
() -> mock(SmartReplyController.class),
mock(ConversationNotificationProcessor.class),
+ mock(MediaFeatureFlag.class),
mock(Executor.class));
contentBinder.setInflateSynchronously(true);
mBindStage = new RowContentBindStage(contentBinder,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
index 45f7c5a..a147c8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
@@ -24,10 +24,10 @@
import androidx.test.filters.SmallTest;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.systemui.tests.R;
import org.junit.Assert;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
index ca6c16f..8c37cf1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
@@ -18,6 +18,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static com.google.common.truth.Truth.assertThat;
@@ -41,6 +42,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -63,6 +65,7 @@
@Mock private IActivityManager mActivityManager;
@Mock private SysuiStatusBarStateController mStatusBarStateController;
@Mock private ConfigurationController mConfigurationController;
+ @Mock private KeyguardViewMediator mKeyguardViewMediator;
@Mock private KeyguardBypassController mKeyguardBypassController;
@Mock private SysuiColorExtractor mColorExtractor;
@Mock ColorExtractor.GradientColors mGradientColors;
@@ -79,8 +82,8 @@
mNotificationShadeWindowController = new NotificationShadeWindowController(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
- mConfigurationController, mKeyguardBypassController, mColorExtractor,
- mDumpManager);
+ mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
+ mColorExtractor, mDumpManager);
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowController.attach();
@@ -120,6 +123,17 @@
}
@Test
+ public void attach_visibleWithWallpaper() {
+ clearInvocations(mWindowManager);
+ when(mKeyguardViewMediator.isShowingAndNotOccluded()).thenReturn(true);
+ mNotificationShadeWindowController.attach();
+
+ verify(mNotificationShadeWindowView).setVisibility(eq(View.VISIBLE));
+ verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture());
+ assertThat((mLayoutParameters.getValue().flags & FLAG_SHOW_WALLPAPER) != 0).isTrue();
+ }
+
+ @Test
public void setBackgroundBlurRadius_expandedWithBlurs() {
mNotificationShadeWindowController.setBackgroundBlurRadius(10);
verify(mNotificationShadeWindowView).setVisibility(eq(View.VISIBLE));
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/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
index 5ce209b..4d6922c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
@@ -15,12 +15,13 @@
package com.android.systemui.statusbar.policy;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import android.app.AppOpsManager;
import android.content.Intent;
import android.location.LocationManager;
import android.testing.AndroidTestingRunner;
@@ -31,12 +32,15 @@
import com.android.systemui.BootCompleteCache;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.appops.AppOpsController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -46,11 +50,15 @@
private LocationControllerImpl mLocationController;
private TestableLooper mTestableLooper;
+ @Mock private AppOpsController mAppOpsController;
+
@Before
public void setup() {
+ MockitoAnnotations.initMocks(this);
+
mTestableLooper = TestableLooper.get(this);
mLocationController = spy(new LocationControllerImpl(mContext,
- mTestableLooper.getLooper(),
+ mAppOpsController,
mTestableLooper.getLooper(),
mock(BroadcastDispatcher.class),
mock(BootCompleteCache.class)));
@@ -67,12 +75,12 @@
mLocationController.addCallback(callback);
mLocationController.addCallback(mock(LocationChangeCallback.class));
- when(mLocationController.areActiveHighPowerLocationRequests()).thenReturn(false);
- mLocationController.onReceive(mContext, new Intent(
- LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION));
- when(mLocationController.areActiveHighPowerLocationRequests()).thenReturn(true);
- mLocationController.onReceive(mContext, new Intent(
- LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION));
+ doReturn(false).when(mLocationController).areActiveHighPowerLocationRequests();
+ mLocationController.onActiveStateChanged(AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, 0,
+ "", false);
+ doReturn(true).when(mLocationController).areActiveHighPowerLocationRequests();
+ mLocationController.onActiveStateChanged(AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, 0,
+ "", true);
mTestableLooper.processAllMessages();
}
@@ -107,11 +115,22 @@
LocationChangeCallback callback = mock(LocationChangeCallback.class);
mLocationController.addCallback(callback);
+
+ mTestableLooper.processAllMessages();
+
mLocationController.onReceive(mContext, new Intent(LocationManager.MODE_CHANGED_ACTION));
mTestableLooper.processAllMessages();
verify(callback, times(2)).onLocationSettingsChanged(anyBoolean());
+
+ doReturn(true).when(mLocationController).areActiveHighPowerLocationRequests();
+ mLocationController.onActiveStateChanged(AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, 0,
+ "", true);
+
+ mTestableLooper.processAllMessages();
+
+ verify(callback, times(1)).onLocationActiveChanged(anyBoolean());
}
@Test
@@ -124,6 +143,8 @@
verify(callback).onLocationSettingsChanged(anyBoolean());
mLocationController.removeCallback(callback);
+ mTestableLooper.processAllMessages();
+
mLocationController.onReceive(mContext, new Intent(LocationManager.MODE_CHANGED_ACTION));
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java
new file mode 100644
index 0000000..8c92482
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.concurrency;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Implementation of {@link ThreadFactory} that returns {@link FakeExecutor} where it can.
+ */
+public class FakeThreadFactory implements ThreadFactory {
+ private final FakeExecutor mFakeExecutor;
+
+ public FakeThreadFactory(FakeExecutor fakeExecutor) {
+ mFakeExecutor = fakeExecutor;
+ }
+
+ @Override
+ public Executor buildExecutorOnNewThread(String threadName) {
+ return mFakeExecutor;
+ }
+
+ @Override
+ public DelayableExecutor buildDelayableExecutorOnNewThread(String threadName) {
+ return mFakeExecutor;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
index 9149599..0d8dd2c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
@@ -23,15 +23,16 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.hardware.SensorEventListener;
-import android.os.Handler;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.concurrency.FakeThreadFactory;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -39,20 +40,20 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
public class AsyncSensorManagerTest extends SysuiTestCase {
private AsyncSensorManager mAsyncSensorManager;
private SensorEventListener mListener;
private FakeSensorManager.FakeProximitySensor mSensor;
private PluginManager mPluginManager;
+ private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
@Before
public void setUp() throws Exception {
mPluginManager = mock(PluginManager.class);
FakeSensorManager fakeSensorManager = new FakeSensorManager(mContext);
mAsyncSensorManager = new AsyncSensorManager(
- fakeSensorManager, mPluginManager, new Handler());
+ fakeSensorManager, new FakeThreadFactory(mFakeExecutor), mPluginManager);
mSensor = fakeSensorManager.getFakeProximitySensor();
mListener = mock(SensorEventListener.class);
}
@@ -99,6 +100,6 @@
}
public void waitUntilRequestsCompleted() {
- TestableLooper.get(this).processAllMessages();
+ mFakeExecutor.runAllReady();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java
index 8ba7d62..d3a35a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java
@@ -20,12 +20,14 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import android.os.Handler;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.Assert;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.concurrency.FakeThreadFactory;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -33,21 +35,20 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
public class ThresholdSensorImplTest extends SysuiTestCase {
private ThresholdSensorImpl mThresholdSensor;
private FakeSensorManager mSensorManager;
private AsyncSensorManager mAsyncSensorManager;
private FakeSensorManager.FakeProximitySensor mFakeProximitySensor;
+ private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
@Before
public void setUp() throws Exception {
- allowTestableLooperAsMainThread();
mSensorManager = new FakeSensorManager(getContext());
mAsyncSensorManager = new AsyncSensorManager(
- mSensorManager, null, new Handler());
+ mSensorManager, new FakeThreadFactory(mFakeExecutor), null);
mFakeProximitySensor = mSensorManager.getFakeProximitySensor();
ThresholdSensorImpl.Builder thresholdSensorBuilder = new ThresholdSensorImpl.Builder(
@@ -60,6 +61,7 @@
@Test
public void testSingleListener() {
+ Assert.setTestThread(Thread.currentThread());
TestableListener listener = new TestableListener();
assertFalse(mThresholdSensor.isRegistered());
@@ -81,6 +83,7 @@
@Test
public void testMultiListener() {
+ Assert.setTestThread(Thread.currentThread());
TestableListener listenerA = new TestableListener();
TestableListener listenerB = new TestableListener();
@@ -114,6 +117,7 @@
@Test
public void testDuplicateListener() {
+ Assert.setTestThread(Thread.currentThread());
TestableListener listenerA = new TestableListener();
assertFalse(mThresholdSensor.isRegistered());
@@ -138,6 +142,7 @@
}
@Test
public void testUnregister() {
+ Assert.setTestThread(Thread.currentThread());
TestableListener listener = new TestableListener();
assertFalse(mThresholdSensor.isRegistered());
@@ -157,6 +162,7 @@
@Test
public void testPauseAndResume() {
+ Assert.setTestThread(Thread.currentThread());
TestableListener listener = new TestableListener();
assertFalse(mThresholdSensor.isRegistered());
@@ -199,6 +205,7 @@
@Test
public void testAlertListeners() {
+ Assert.setTestThread(Thread.currentThread());
TestableListener listenerA = new TestableListener();
TestableListener listenerB = new TestableListener();
@@ -230,6 +237,7 @@
@Test
public void testHysteresis() {
+ Assert.setTestThread(Thread.currentThread());
float lowValue = 10f;
float highValue = 100f;
FakeSensorManager.FakeGenericSensor sensor = mSensorManager.getFakeLightSensor();
@@ -278,6 +286,7 @@
@Test
public void testAlertAfterPause() {
+ Assert.setTestThread(Thread.currentThread());
TestableListener listener = new TestableListener();
mThresholdSensor.register(listener);
@@ -307,7 +316,7 @@
}
private void waitForSensorManager() {
- TestableLooper.get(this).processAllMessages();
+ mFakeExecutor.runAllReady();
}
}
diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 74df113..e10bab4 100644
--- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -42,6 +42,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.system.Os;
import android.util.Log;
@@ -224,9 +225,19 @@
}
+ private boolean isAdbOverNetwork() {
+ // If adb TCP port opened, this test may running by adb over network.
+ return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1)
+ || (SystemProperties.getInt("service.adb.tcp.port", -1) > -1);
+ }
+
@Test
public void testPhysicalEthernet() throws Exception {
assumeTrue(mEm.isAvailable());
+ // Do not run this test if adb is over network and ethernet is connected.
+ // It is likely the adb run over ethernet, the adb would break when ethernet is switching
+ // from client mode to server mode. See b/160389275.
+ assumeFalse(isAdbOverNetwork());
// Get an interface to use.
final String iface = mTetheredInterfaceRequester.getInterface();
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/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index c82dff2..a4c5a23 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -295,6 +295,7 @@
mEventTypes = info.eventTypes;
mFeedbackType = info.feedbackType;
String[] packageNames = info.packageNames;
+ mPackageNames.clear();
if (packageNames != null) {
mPackageNames.addAll(Arrays.asList(packageNames));
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 468e93a..669bb24 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -747,6 +747,13 @@
* Dumps all {@link AccessibilityWindowInfo}s here.
*/
void dumpLocked(FileDescriptor fd, final PrintWriter pw, String[] args) {
+ pw.append("Global Info [ ");
+ pw.println("Top focused display Id = " + mTopFocusedDisplayId);
+ pw.println(" Active Window Id = " + mActiveWindowId);
+ pw.println(" Top Focused Window Id = " + mTopFocusedWindowId);
+ pw.println(" Accessibility Focused Window Id = " + mAccessibilityFocusedWindowId
+ + " ]");
+ pw.println();
if (mWindows != null) {
final int windowCount = mWindows.size();
for (int j = 0; j < windowCount; j++) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
index 68eeb0a..b2daae4 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
@@ -103,6 +103,8 @@
private boolean mDestroyed = false;
@GuardedBy("mLock")
private boolean mPreviousHasNonPinSuggestionShow;
+ @GuardedBy("mLock")
+ private boolean mImeSessionInvalidated = false;
AutofillInlineSuggestionsRequestSession(
@NonNull InputMethodManagerInternal inputMethodManagerInternal, int userId,
@@ -157,7 +159,7 @@
Slog.d(TAG,
"onInlineSuggestionsResponseLocked called for:" + inlineFillUi.getAutofillId());
}
- if (mImeRequest == null || mResponseCallback == null) {
+ if (mImeRequest == null || mResponseCallback == null || mImeSessionInvalidated) {
return false;
}
// TODO(b/151123764): each session should only correspond to one field.
@@ -191,6 +193,7 @@
if (mDestroyed) {
return;
}
+ mImeSessionInvalidated = false;
if (sDebug) Slog.d(TAG, "onCreateInlineSuggestionsRequestLocked called: " + mAutofillId);
mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(mUserId,
new InlineSuggestionsRequestInfo(mComponentName, mAutofillId, mUiExtras),
@@ -291,6 +294,7 @@
return;
}
mImeRequestReceived = true;
+ mImeSessionInvalidated = false;
if (request != null && callback != null) {
mImeRequest = request;
@@ -346,6 +350,20 @@
}
}
+ /**
+ * Handles the IME session status received from the IME.
+ *
+ * <p> Should only be invoked in the {@link #mHandler} thread.
+ */
+ private void handleOnReceiveImeSessionInvalidated() {
+ synchronized (mLock) {
+ if (mDestroyed) {
+ return;
+ }
+ mImeSessionInvalidated = true;
+ }
+ }
+
private static final class InlineSuggestionsRequestCallbackImpl extends
IInlineSuggestionsRequestCallback.Stub {
@@ -433,6 +451,18 @@
session, false, false));
}
}
+
+ @BinderThread
+ @Override
+ public void onInlineSuggestionsSessionInvalidated() throws RemoteException {
+ if (sDebug) Slog.d(TAG, "onInlineSuggestionsSessionInvalidated() called.");
+ final AutofillInlineSuggestionsRequestSession session = mSession.get();
+ if (session != null) {
+ session.mHandler.sendMessage(obtainMessage(
+ AutofillInlineSuggestionsRequestSession
+ ::handleOnReceiveImeSessionInvalidated, session));
+ }
+ }
}
private static boolean match(@Nullable AutofillId autofillId,
diff --git a/services/backup/OWNERS b/services/backup/OWNERS
index ba61d1c0..7c7e742 100644
--- a/services/backup/OWNERS
+++ b/services/backup/OWNERS
@@ -1,7 +1,12 @@
# Bug component: 656484
+aabhinav@google.com
alsutton@google.com
bryanmawhinney@google.com
+jstemmer@google.com
nathch@google.com
+niagra@google.com
+niamhfw@google.com
+philippov@google.com
rthakohov@google.com
tobiast@google.com
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 766e36d..84bd59b 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -43,8 +43,6 @@
import static android.os.storage.OnObbStateChangeListener.MOUNTED;
import static android.os.storage.OnObbStateChangeListener.UNMOUNTED;
import static android.os.storage.StorageManager.PROP_FORCED_SCOPED_STORAGE_WHITELIST;
-import static android.os.storage.StorageManager.PROP_FUSE;
-import static android.os.storage.StorageManager.PROP_SETTINGS_FUSE;
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.readLongAttribute;
@@ -131,7 +129,6 @@
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.DataUnit;
-import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -233,13 +230,6 @@
*/
private static final String ISOLATED_STORAGE_ENABLED = "isolated_storage_enabled";
- /**
- * If {@code 1}, enables FuseDaemon to intercept file system ops. If {@code -1},
- * disables FuseDaemon. If {@code 0}, uses the default value from the build system.
- */
- private static final String FUSE_ENABLED = "fuse_enabled";
- private static final boolean DEFAULT_FUSE_ENABLED = true;
-
@GuardedBy("mLock")
private final Set<Integer> mFuseMountedUser = new ArraySet<>();
@@ -609,8 +599,6 @@
// Not guarded by a lock.
private final StorageSessionController mStorageSessionController;
- private final boolean mIsFuseEnabled;
-
private final boolean mVoldAppDataIsolationEnabled;
@GuardedBy("mLock")
@@ -926,7 +914,6 @@
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
mContext.getMainExecutor(), (properties) -> {
refreshIsolatedStorageSettings();
- refreshFuseSettings();
});
refreshIsolatedStorageSettings();
}
@@ -993,27 +980,6 @@
}
/**
- * The most recent flag change takes precedence. Change fuse Settings flag if Device Config is
- * changed. Settings flag change will in turn change fuse system property (persist.sys.fuse)
- * whenever the user reboots.
- */
- private void refreshFuseSettings() {
- int isFuseEnabled = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
- FUSE_ENABLED, 0);
- if (isFuseEnabled == 1) {
- Slog.d(TAG, "Device Config flag for FUSE is enabled, turn Settings fuse flag on");
- SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX
- + FeatureFlagUtils.SETTINGS_FUSE_FLAG, "true");
- } else if (isFuseEnabled == -1) {
- Slog.d(TAG, "Device Config flag for FUSE is disabled, turn Settings fuse flag off");
- SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX
- + FeatureFlagUtils.SETTINGS_FUSE_FLAG, "false");
- }
- // else, keep the build config.
- // This can be overridden by direct adjustment of persist.sys.fflag.override.settings_fuse
- }
-
- /**
* MediaProvider has a ton of code that makes assumptions about storage
* paths never changing, so we outright kill them to pick up new state.
*/
@@ -1091,13 +1057,9 @@
final UserManager userManager = mContext.getSystemService(UserManager.class);
final List<UserInfo> users = userManager.getUsers();
- if (mIsFuseEnabled) {
- mStorageSessionController.onReset(mVold, () -> {
- mHandler.removeCallbacksAndMessages(null);
- });
- } else {
- killMediaProvider(users);
- }
+ mStorageSessionController.onReset(mVold, () -> {
+ mHandler.removeCallbacksAndMessages(null);
+ });
final int[] systemUnlockedUsers;
synchronized (mLock) {
@@ -1490,8 +1452,7 @@
final ActivityManagerInternal amInternal =
LocalServices.getService(ActivityManagerInternal.class);
- if (mIsFuseEnabled && vol.mountUserId >= 0
- && !amInternal.isUserRunning(vol.mountUserId, 0)) {
+ if (vol.mountUserId >= 0 && !amInternal.isUserRunning(vol.mountUserId, 0)) {
Slog.d(TAG, "Ignoring volume " + vol.getId() + " because user "
+ Integer.toString(vol.mountUserId) + " is no longer running.");
return;
@@ -1803,12 +1764,8 @@
SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString(
SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, true)));
- // If there is no value in the property yet (first boot after data wipe), this value may be
- // incorrect until #updateFusePropFromSettings where we set the correct value and reboot if
- // different
- mIsFuseEnabled = SystemProperties.getBoolean(PROP_FUSE, DEFAULT_FUSE_ENABLED);
- mVoldAppDataIsolationEnabled = mIsFuseEnabled && SystemProperties.getBoolean(
- ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
+ mVoldAppDataIsolationEnabled = SystemProperties.getBoolean(
+ ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
mContext = context;
mResolver = mContext.getContentResolver();
mCallbacks = new Callbacks(FgThread.get().getLooper());
@@ -1821,7 +1778,7 @@
// Add OBB Action Handler to StorageManagerService thread.
mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
- mStorageSessionController = new StorageSessionController(mContext, mIsFuseEnabled);
+ mStorageSessionController = new StorageSessionController(mContext);
mInstaller = new Installer(mContext);
mInstaller.onStart();
@@ -1869,26 +1826,6 @@
PackageManager.FEATURE_AUTOMOTIVE);
}
- /**
- * Checks if user changed the persistent settings_fuse flag from Settings UI
- * and updates PROP_FUSE (reboots if changed).
- */
- private void updateFusePropFromSettings() {
- boolean settingsFuseFlag = SystemProperties.getBoolean(PROP_SETTINGS_FUSE,
- DEFAULT_FUSE_ENABLED);
- Slog.d(TAG, "FUSE flags. Settings: " + settingsFuseFlag
- + ". Default: " + DEFAULT_FUSE_ENABLED);
-
- if (mIsFuseEnabled != settingsFuseFlag) {
- Slog.i(TAG, "Toggling persist.sys.fuse to " + settingsFuseFlag);
- // Set prop_fuse to match prop_settings_fuse because it is used by native daemons like
- // init, zygote, installd and vold
- SystemProperties.set(PROP_FUSE, Boolean.toString(settingsFuseFlag));
- // Then perform hard reboot to kick policy into place
- mContext.getSystemService(PowerManager.class).reboot("fuse_prop");
- }
- }
-
private void start() {
connectStoraged();
connectVold();
@@ -1987,15 +1924,6 @@
if (provider != null) {
mExternalStorageAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid);
}
-
- if (!mIsFuseEnabled) {
- try {
- mIAppOpsService.startWatchingMode(OP_REQUEST_INSTALL_PACKAGES, null,
- mAppOpsCallback);
- mIAppOpsService.startWatchingMode(OP_LEGACY_STORAGE, null, mAppOpsCallback);
- } catch (RemoteException e) {
- }
- }
}
private ProviderInfo getProviderInfo(String authority) {
@@ -2071,7 +1999,6 @@
private void bootCompleted() {
mBootCompleted = true;
mHandler.obtainMessage(H_BOOT_COMPLETED).sendToTarget();
- updateFusePropFromSettings();
}
private void handleBootCompleted() {
@@ -4269,14 +4196,14 @@
return Zygote.MOUNT_EXTERNAL_NONE;
}
- if (mIsFuseEnabled && mStorageManagerInternal.isExternalStorageService(uid)) {
+ if (mStorageManagerInternal.isExternalStorageService(uid)) {
// Determine if caller requires pass_through mount; note that we do this for
// all processes that share a UID with MediaProvider; but this is fine, since
// those processes anyway share the same rights as MediaProvider.
return Zygote.MOUNT_EXTERNAL_PASS_THROUGH;
}
- if (mIsFuseEnabled && (mDownloadsAuthorityAppId == UserHandle.getAppId(uid)
+ if ((mDownloadsAuthorityAppId == UserHandle.getAppId(uid)
|| mExternalStorageAuthorityAppId == UserHandle.getAppId(uid))) {
// DownloadManager can write in app-private directories on behalf of apps;
// give it write access to Android/
@@ -4286,7 +4213,7 @@
final boolean hasMtp = mIPackageManager.checkUidPermission(ACCESS_MTP, uid) ==
PERMISSION_GRANTED;
- if (mIsFuseEnabled && hasMtp) {
+ if (hasMtp) {
ApplicationInfo ai = mIPackageManager.getApplicationInfo(packageName,
0, UserHandle.getUserId(uid));
if (ai != null && ai.isSignedWithPlatformKey()) {
@@ -4737,7 +4664,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,
@@ -4749,32 +4676,25 @@
public void onAppOpsChanged(int code, int uid, @Nullable String packageName, int mode) {
final long token = Binder.clearCallingIdentity();
try {
- if (mIsFuseEnabled) {
- // When using FUSE, we may need to kill the app if the op changes
- switch(code) {
- case OP_REQUEST_INSTALL_PACKAGES:
- // Always kill regardless of op change, to remount apps /storage
- killAppForOpChange(code, uid, packageName);
- return;
- case OP_MANAGE_EXTERNAL_STORAGE:
- if (mode != MODE_ALLOWED) {
- // Only kill if op is denied, to lose external_storage gid
- // Killing when op is granted to pickup the gid automatically,
- // 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.
- }
- }
- return;
- case OP_LEGACY_STORAGE:
- updateLegacyStorageApps(packageName, uid, mode == MODE_ALLOWED);
- return;
- }
+ // When using FUSE, we may need to kill the app if the op changes
+ switch(code) {
+ case OP_REQUEST_INSTALL_PACKAGES:
+ // Always kill regardless of op change, to remount apps /storage
+ killAppForOpChange(code, uid);
+ return;
+ case OP_MANAGE_EXTERNAL_STORAGE:
+ if (mode != MODE_ALLOWED) {
+ // Only kill if op is denied, to lose external_storage gid
+ // Killing when op is granted to pickup the gid automatically,
+ // 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.
+ killAppForOpChange(code, uid);
+ }
+ return;
+ case OP_LEGACY_STORAGE:
+ updateLegacyStorageApps(packageName, uid, mode == MODE_ALLOWED);
+ return;
}
if (mode == MODE_ALLOWED && (code == OP_READ_EXTERNAL_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/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 4ff9eb11..72f29b4 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -43,6 +43,7 @@
import android.os.IExternalVibratorService;
import android.os.IVibratorService;
import android.os.IVibratorStateListener;
+import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
@@ -70,6 +71,7 @@
import android.view.InputDevice;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -134,7 +136,7 @@
private final SparseArray<VibrationEffect> mFallbackEffects;
private final SparseArray<Integer> mProcStatesCache = new SparseArray<>();
private final WorkSource mTmpWorkSource = new WorkSource();
- private final Handler mH = new Handler();
+ private final Handler mH;
private final Object mLock = new Object();
private final Context mContext;
@@ -147,6 +149,7 @@
private Vibrator mVibrator;
private SettingsObserver mSettingObserver;
+ private final NativeWrapper mNativeWrapper;
private volatile VibrateThread mThread;
// mInputDeviceVibrators lock should be acquired after mLock, if both are
@@ -208,7 +211,12 @@
}
};
- private class Vibration implements IBinder.DeathRecipient {
+ /**
+ * Holder for a vibration to be played. This class can be shared with native methods for
+ * hardware callback support.
+ */
+ @VisibleForTesting
+ public final class Vibration implements IBinder.DeathRecipient {
public final IBinder token;
// Start time in CLOCK_BOOTTIME base.
public final long startTime;
@@ -248,9 +256,9 @@
}
}
- // Called by native
- @SuppressWarnings("unused")
- private void onComplete() {
+ /** Callback for when vibration is complete, to be called by native. */
+ @VisibleForTesting
+ public void onComplete() {
synchronized (mLock) {
if (this == mCurrentVibration) {
doCancelVibrateLocked();
@@ -354,15 +362,23 @@
}
VibratorService(Context context) {
- vibratorInit();
+ this(context, new Injector());
+ }
+
+ @VisibleForTesting
+ VibratorService(Context context, Injector injector) {
+ mNativeWrapper = injector.getNativeWrapper();
+ mH = injector.createHandler(Looper.myLooper());
+
+ mNativeWrapper.vibratorInit();
// Reset the hardware to a default state, in case this is a runtime
// restart instead of a fresh boot.
- vibratorOff();
+ mNativeWrapper.vibratorOff();
- mSupportsAmplitudeControl = vibratorSupportsAmplitudeControl();
- mSupportsExternalControl = vibratorSupportsExternalControl();
- mSupportedEffects = asList(vibratorGetSupportedEffects());
- mCapabilities = vibratorGetCapabilities();
+ mSupportsAmplitudeControl = mNativeWrapper.vibratorSupportsAmplitudeControl();
+ mSupportsExternalControl = mNativeWrapper.vibratorSupportsExternalControl();
+ mSupportedEffects = asList(mNativeWrapper.vibratorGetSupportedEffects());
+ mCapabilities = mNativeWrapper.vibratorGetCapabilities();
mContext = context;
PowerManager pm = context.getSystemService(PowerManager.class);
@@ -419,7 +435,7 @@
mScaleLevels.put(SCALE_HIGH, new ScaleLevel(SCALE_FACTOR_HIGH));
mScaleLevels.put(SCALE_VERY_HIGH, new ScaleLevel(SCALE_FACTOR_VERY_HIGH));
- ServiceManager.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
+ injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
}
private VibrationEffect createEffectFromResource(int resId) {
@@ -642,7 +658,7 @@
if (effect == null) {
synchronized (mLock) {
mAlwaysOnEffects.delete(alwaysOnId);
- vibratorAlwaysOnDisable(alwaysOnId);
+ mNativeWrapper.vibratorAlwaysOnDisable(alwaysOnId);
}
} else {
if (!verifyVibrationEffect(effect)) {
@@ -1198,11 +1214,11 @@
private void updateAlwaysOnLocked(int id, Vibration vib) {
final int intensity = getCurrentIntensityLocked(vib);
if (!shouldVibrate(vib, intensity)) {
- vibratorAlwaysOnDisable(id);
+ mNativeWrapper.vibratorAlwaysOnDisable(id);
} else {
final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect;
final int strength = intensityToEffectStrength(intensity);
- vibratorAlwaysOnEnable(id, prebaked.getId(), strength);
+ mNativeWrapper.vibratorAlwaysOnEnable(id, prebaked.getId(), strength);
}
}
@@ -1238,7 +1254,7 @@
//synchronized (mInputDeviceVibrators) {
// return !mInputDeviceVibrators.isEmpty() || vibratorExists();
//}
- return vibratorExists();
+ return mNativeWrapper.vibratorExists();
}
private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs) {
@@ -1262,7 +1278,7 @@
// Note: ordering is important here! Many haptic drivers will reset their
// amplitude when enabled, so we always have to enable first, then set the
// amplitude.
- vibratorOn(millis);
+ mNativeWrapper.vibratorOn(millis);
doVibratorSetAmplitude(amplitude);
}
}
@@ -1273,7 +1289,7 @@
private void doVibratorSetAmplitude(int amplitude) {
if (mSupportsAmplitudeControl) {
- vibratorSetAmplitude(amplitude);
+ mNativeWrapper.vibratorSetAmplitude(amplitude);
}
}
@@ -1291,7 +1307,7 @@
mInputDeviceVibrators.get(i).cancel();
}
} else {
- vibratorOff();
+ mNativeWrapper.vibratorOff();
}
}
} finally {
@@ -1310,7 +1326,7 @@
}
// Input devices don't support prebaked effect, so skip trying it with them.
if (!usingInputDeviceVibrators) {
- long duration = vibratorPerformEffect(prebaked.getId(),
+ long duration = mNativeWrapper.vibratorPerformEffect(prebaked.getId(),
prebaked.getEffectStrength(), vib,
hasCapability(IVibrator.CAP_PERFORM_CALLBACK));
long timeout = duration;
@@ -1363,7 +1379,7 @@
PrimitiveEffect[] primitiveEffects =
composed.getPrimitiveEffects().toArray(new PrimitiveEffect[0]);
- vibratorPerformComposedEffect(primitiveEffects, vib);
+ mNativeWrapper.vibratorPerformComposedEffect(primitiveEffects, vib);
// Composed effects don't actually give us an estimated duration, so we just guess here.
noteVibratorOnLocked(vib.uid, 10 * primitiveEffects.length);
@@ -1454,7 +1470,7 @@
}
}
mVibratorUnderExternalControl = externalControl;
- vibratorSetExternalControl(externalControl);
+ mNativeWrapper.vibratorSetExternalControl(externalControl);
}
private void dumpInternal(PrintWriter pw) {
@@ -1688,6 +1704,100 @@
}
}
+ /** Wrapper around the static-native methods of {@link VibratorService} for tests. */
+ @VisibleForTesting
+ public static class NativeWrapper {
+
+ /** Checks if vibrator exists on device. */
+ public boolean vibratorExists() {
+ return VibratorService.vibratorExists();
+ }
+
+ /** Initializes connection to vibrator HAL service. */
+ public void vibratorInit() {
+ VibratorService.vibratorInit();
+ }
+
+ /** Turns vibrator on for given time. */
+ public void vibratorOn(long milliseconds) {
+ VibratorService.vibratorOn(milliseconds);
+ }
+
+ /** Turns vibrator off. */
+ public void vibratorOff() {
+ VibratorService.vibratorOff();
+ }
+
+ /** Returns true if vibrator supports {@link #vibratorSetAmplitude(int)}. */
+ public boolean vibratorSupportsAmplitudeControl() {
+ return VibratorService.vibratorSupportsAmplitudeControl();
+ }
+
+ /** Sets the amplitude for the vibrator to run. */
+ public void vibratorSetAmplitude(int amplitude) {
+ VibratorService.vibratorSetAmplitude(amplitude);
+ }
+
+ /** Returns all predefined effects supported by the device vibrator. */
+ public int[] vibratorGetSupportedEffects() {
+ return VibratorService.vibratorGetSupportedEffects();
+ }
+
+ /** Turns vibrator on to perform one of the supported effects. */
+ public long vibratorPerformEffect(long effect, long strength, Vibration vibration,
+ boolean withCallback) {
+ return VibratorService.vibratorPerformEffect(effect, strength, vibration, withCallback);
+ }
+
+ /** Turns vibrator on to perform one of the supported composed effects. */
+ public void vibratorPerformComposedEffect(
+ VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration) {
+ VibratorService.vibratorPerformComposedEffect(effect, vibration);
+ }
+
+ /** Returns true if vibrator supports {@link #vibratorSetExternalControl(boolean)}. */
+ public boolean vibratorSupportsExternalControl() {
+ return VibratorService.vibratorSupportsExternalControl();
+ }
+
+ /** Enabled the device vibrator to be controlled by another service. */
+ public void vibratorSetExternalControl(boolean enabled) {
+ VibratorService.vibratorSetExternalControl(enabled);
+ }
+
+ /** Returns all capabilities of the device vibrator. */
+ public long vibratorGetCapabilities() {
+ return VibratorService.vibratorGetCapabilities();
+ }
+
+ /** Enable always-on vibration with given id and effect. */
+ public void vibratorAlwaysOnEnable(long id, long effect, long strength) {
+ VibratorService.vibratorAlwaysOnEnable(id, effect, strength);
+ }
+
+ /** Disable always-on vibration for given id. */
+ public void vibratorAlwaysOnDisable(long id) {
+ VibratorService.vibratorAlwaysOnDisable(id);
+ }
+ }
+
+ /** Point of injection for test dependencies */
+ @VisibleForTesting
+ static class Injector {
+
+ NativeWrapper getNativeWrapper() {
+ return new NativeWrapper();
+ }
+
+ Handler createHandler(Looper looper) {
+ return new Handler(looper);
+ }
+
+ void addService(String name, IBinder service) {
+ ServiceManager.addService(name, service);
+ }
+ }
+
BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a1ea4b7..25e6eb6e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -232,11 +232,9 @@
import android.database.ContentObserver;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerInternal;
-import android.location.LocationManager;
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 +2885,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()
@@ -3320,6 +3317,9 @@
@Override
public boolean setProcessMemoryTrimLevel(String process, int userId, int level)
throws RemoteException {
+ if (!isCallerShell()) {
+ throw new SecurityException("Only shell can call it");
+ }
synchronized (this) {
final ProcessRecord app = findProcessLocked(process, userId, "setProcessMemoryTrimLevel");
if (app == null) {
@@ -6358,6 +6358,9 @@
int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
int callingPid, boolean alwaysRestrict, boolean disabledOnly, boolean forcedStandby) {
+ if (mInternal.isPendingTopUid(uid)) {
+ return ActivityManager.APP_START_MODE_NORMAL;
+ }
UidRecord uidRec = mProcessList.getUidRecordLocked(uid);
if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg="
+ packageName + " rec=" + uidRec + " always=" + alwaysRestrict + " idle="
@@ -7464,11 +7467,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 +7585,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);
}
/**
@@ -15868,7 +15862,6 @@
|| Intent.ACTION_FACTORY_RESET.equals(action)
|| AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
|| AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)
- || LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)
|| TelephonyManager.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action)
|| SuggestionSpan.ACTION_SUGGESTION_PICKED.equals(action)
|| AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION.equals(action)
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 04089e3..ebff069 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -110,7 +110,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ProcessMap;
import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.os.RuntimeInit;
import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -155,9 +154,6 @@
static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
"persist.sys.vold_app_data_isolation_enabled";
- // A system property to control if fuse is enabled.
- static final String ANDROID_FUSE_ENABLED = "persist.sys.fuse";
-
// The minimum time we allow between crashes, for us to consider this
// application to be bad and stop and its services and reject broadcasts.
static final int MIN_CRASH_INTERVAL = 60 * 1000;
@@ -719,13 +715,8 @@
// want some apps enabled while some apps disabled
mAppDataIsolationEnabled =
SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
- boolean fuseEnabled = SystemProperties.getBoolean(ANDROID_FUSE_ENABLED, false);
- boolean voldAppDataIsolationEnabled = SystemProperties.getBoolean(
- ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
- if (!fuseEnabled && voldAppDataIsolationEnabled) {
- Slog.e(TAG, "Fuse is not enabled while vold app data isolation is enabled");
- }
- mVoldAppDataIsolationEnabled = fuseEnabled && voldAppDataIsolationEnabled;
+ mVoldAppDataIsolationEnabled = SystemProperties.getBoolean(
+ ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
mAppDataIsolationWhitelistedApps = new ArrayList<>(
SystemConfig.getInstance().getAppDataIsolationWhitelistedApps());
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/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..68cc884 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,33 @@
private final VibrationEffect mSuccessVibrationEffect;
private final VibrationEffect mErrorVibrationEffect;
- AcquisitionClient(@NonNull FinishCallback finishCallback, @NonNull Context context,
+ /**
+ * Stops the HAL operation specific to the ClientMonitor subclass.
+ */
+ protected abstract void stopHalOperation();
+
+ AcquisitionClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
- boolean restricted, @NonNull String owner, int cookie, int sensorId, int statsModality,
+ @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, lazyDaemon, 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 {
@@ -68,7 +83,7 @@
} catch (RemoteException e) {
Slog.w(TAG, "Failed to invoke sendError", e);
}
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
/**
@@ -100,7 +115,7 @@
}
} catch (RemoteException e) {
Slog.w(TAG, "Failed to invoke sendAcquired", e);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
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..df836d5 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,30 @@
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,
+ protected boolean mAuthAttempted;
+
+ public AuthenticationClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
@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);
+ super(context, lazyDaemon, 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 +149,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 {
@@ -175,7 +180,7 @@
}
} catch (RemoteException e) {
Slog.e(TAG, "Unable to notify listener, finishing", e);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
@@ -183,7 +188,20 @@
* Start authentication
*/
@Override
- public void start() {
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(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 +211,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..9f5abd8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java
@@ -34,17 +34,12 @@
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricService;
-import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.fingerprint.Fingerprint;
import android.os.Binder;
-import android.os.Bundle;
-import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
import android.os.IHwBinder;
-import android.os.IRemoteCallback;
import android.os.Looper;
-import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -71,7 +66,7 @@
*
* @hide
*/
-public abstract class BiometricServiceBase extends SystemService
+public abstract class BiometricServiceBase<T> extends SystemService
implements IHwBinder.DeathRecipient {
protected static final boolean DEBUG = true;
@@ -82,12 +77,9 @@
private final Context mContext;
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();
- private final ArrayList<LockoutResetMonitor> mLockoutMonitors = new ArrayList<>();
protected final IStatusBarService mStatusBarService;
protected final Map<Integer, Long> mAuthenticatorIds =
@@ -110,26 +102,19 @@
}
};
- protected final ClientMonitor.FinishCallback mClientFinishCallback = clientMonitor -> {
- if (clientMonitor instanceof RemovalConsumer) {
- // When the last biometric of a group is removed, update the authenticator id.
- // Note that 1) multiple ClientMonitors may be cause onRemoved (e.g. internal cleanup),
- // and 2) updateActiveGroup updates/relies on global state, so there's no good way to
- // compartmentalize this yet.
- final int userId = clientMonitor.getTargetUserId();
- if (!hasEnrolledBiometrics(userId)) {
- Slog.d(getTag(), "Last biometric removed for user: " + userId
- + ", updating active group");
- updateActiveGroup(userId, null);
- }
- }
-
+ protected final ClientMonitor.FinishCallback mClientFinishCallback =
+ (clientMonitor, success) -> {
removeClient(clientMonitor);
+ // 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 (clientMonitor instanceof EnrollClient) {
+ updateActiveGroup(clientMonitor.getTargetUserId());
+ }
};
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 +125,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 +143,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,72 +242,13 @@
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);
}
}
-
-
- private final class LockoutResetMonitor implements IBinder.DeathRecipient {
- private static final long WAKELOCK_TIMEOUT_MS = 2000;
- private final IBiometricServiceLockoutResetCallback mCallback;
- private final PowerManager.WakeLock mWakeLock;
-
- public LockoutResetMonitor(IBiometricServiceLockoutResetCallback callback) {
- mCallback = callback;
- mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- "lockout reset callback");
- try {
- mCallback.asBinder().linkToDeath(LockoutResetMonitor.this, 0);
- } catch (RemoteException e) {
- Slog.w(getTag(), "caught remote exception in linkToDeath", e);
- }
- }
-
- public void sendLockoutReset() {
- if (mCallback != null) {
- try {
- mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
- mCallback.onLockoutReset(new IRemoteCallback.Stub() {
- @Override
- public void sendResult(Bundle data) throws RemoteException {
- releaseWakelock();
- }
- });
- } catch (DeadObjectException e) {
- Slog.w(getTag(), "Death object while invoking onLockoutReset: ", e);
- mHandler.post(mRemoveCallbackRunnable);
- } catch (RemoteException e) {
- Slog.w(getTag(), "Failed to invoke onLockoutReset: ", e);
- releaseWakelock();
- }
- }
- }
-
- private final Runnable mRemoveCallbackRunnable = new Runnable() {
- @Override
- public void run() {
- releaseWakelock();
- removeLockoutResetCallback(LockoutResetMonitor.this);
- }
- };
-
- @Override
- public void binderDied() {
- Slog.e(getTag(), "Lockout reset callback binder died");
- mHandler.post(mRemoveCallbackRunnable);
- }
-
- private void releaseWakelock() {
- if (mWakeLock.isHeld()) {
- mWakeLock.release();
- }
- }
- }
-
/**
* Initializes the system service.
* <p>
@@ -338,8 +268,6 @@
mKeyguardPackage = keyguardComponent != null ? keyguardComponent.getPackageName() : null;
mAppOps = context.getSystemService(AppOpsManager.class);
mActivityTaskManager = ActivityTaskManager.getService();
- mPowerManager = mContext.getSystemService(PowerManager.class);
- mUserManager = UserManager.get(mContext);
mPerformanceTracker = PerformanceTracker.getInstanceForSensorId(getSensorId());
}
@@ -450,15 +378,7 @@
}
final EnrollClient enrollClient = (EnrollClient) client;
-
- if (enrollClient.onEnrollResult(identifier, remaining)) {
- removeClient(enrollClient);
- // 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);
- }
- }
+ enrollClient.onEnrollResult(identifier, remaining);
}
protected void handleError(int error, int vendorCode) {
@@ -520,7 +440,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 +466,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 +542,14 @@
});
}
- 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<? extends BiometricAuthenticator.Identifier, T> client) {
mHandler.post(() -> {
if (DEBUG) {
Slog.v(getTag(), "Cleaning up templates for user("
@@ -639,34 +560,12 @@
}
// 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 */);
}
- protected void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback) {
- if (callback == null) {
- Slog.w(getTag(), "Null LockoutResetCallback");
- return;
- }
- mHandler.post(() -> {
- final LockoutResetMonitor monitor = new LockoutResetMonitor(callback);
- if (!mLockoutMonitors.contains(monitor)) {
- mLockoutMonitors.add(monitor);
- }
- });
- }
-
/**
* Helper methods.
*/
@@ -751,7 +650,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 +710,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 +722,16 @@
return;
}
- mCurrentClient.start();
+ final T daemon = mCurrentClient.getFreshDaemon();
+ if (daemon == null) {
+ Slog.e(getTag(), "Daemon null, unable to start: "
+ + mCurrentClient.getClass().getSimpleName());
+ mCurrentClient.unableToStart();
+ mCurrentClient = null;
+ return;
+ }
+
+ mCurrentClient.start(mClientFinishCallback);
notifyClientActiveCallbacks(true);
}
@@ -855,9 +763,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 +775,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 +817,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,51 +833,16 @@
if (getCurrentClient() instanceof InternalCleanupClient) {
Slog.w(getTag(), "User switched while performing cleanup");
}
- updateActiveGroup(userId, null);
+ updateActiveGroup(userId);
doTemplateCleanupForUser(userId);
}
- protected void notifyLockoutResetMonitors() {
- for (int i = 0; i < mLockoutMonitors.size(); i++) {
- mLockoutMonitors.get(i).sendLockoutReset();
- }
- }
-
- /**
- * @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();
}
@@ -990,9 +851,4 @@
Slog.w(getTag(), "Failed to listen for user switching event" ,e);
}
}
-
- 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..3f301cc 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;
@@ -47,35 +47,42 @@
* implementation.
*
* @param clientMonitor Reference of the ClientMonitor that finished.
+ * @param success True if the operation completed successfully.
*/
- void onClientFinished(ClientMonitor clientMonitor);
+ void onClientFinished(ClientMonitor clientMonitor, boolean success);
}
- protected final FinishCallback mFinishCallback;
+ /**
+ * Interface that allows ClientMonitor subclasses to retrieve a fresh instance to the HAL.
+ */
+ public interface LazyDaemon<T> {
+ /**
+ * @return A fresh instance to the biometric HAL
+ */
+ T getDaemon();
+ }
- private final Context mContext;
+ @NonNull private final Context mContext;
+ @NonNull protected final LazyDaemon<T> mLazyDaemon;
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 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 lazyDaemon pointer for lazy retrieval of the HAL
* @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 +90,16 @@
* @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, @NonNull LazyDaemon<T> lazyDaemon,
+ @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;
+ mLazyDaemon = lazyDaemon;
mToken = token;
mListener = listener;
mTargetUserId = userId;
- mIsRestricted = restricted;
mOwner = owner;
mCookie = cookie;
mSensorId = sensorId;
@@ -112,21 +118,26 @@
}
/**
+ * 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(FinishCallback)}.
+ */
+ public abstract void unableToStart();
+
+ /**
* Starts the ClientMonitor's lifecycle. Invokes {@link #startHalOperation()} when internal book
* keeping is complete.
+ * @param finishCallback invoked when the operation is complete (succeeds, fails, etc)
*/
- public abstract void start();
+ public void start(@NonNull FinishCallback finishCallback) {
+ mFinishCallback = finishCallback;
+ }
/**
* Starts the HAL operation specific to the ClientMonitor subclass.
*/
protected abstract void startHalOperation();
- /**
- * Stops the HAL operation specific to the ClientMonitor subclass.
- */
- protected abstract void stopHalOperation();
-
public boolean isAlreadyDone() {
return mAlreadyDone;
}
@@ -174,10 +185,6 @@
return mListener;
}
- public final boolean getIsRestricted() {
- return mIsRestricted;
- }
-
public final int getTargetUserId() {
return mTargetUserId;
}
@@ -189,4 +196,8 @@
public final int getSensorId() {
return mSensorId;
}
+
+ public final T getFreshDaemon() {
+ return mLazyDaemon.getDaemon();
+ }
}
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..a3d9677 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,13 +41,13 @@
private long mEnrollmentStartTimeMs;
private boolean mAlreadyCancelled;
- public EnrollClient(@NonNull FinishCallback finishCallback, @NonNull Context context,
+ public EnrollClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull byte[] hardwareAuthToken, boolean restricted, String owner,
- @NonNull BiometricUtils utils, int timeoutSec, int statsModality, int sensorId,
+ @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,
+ super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
+ statsModality, BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN);
mBiometricUtils = utils;
mHardwareAuthToken = Arrays.copyOf(hardwareAuthToken, hardwareAuthToken.length);
@@ -55,40 +55,33 @@
mShouldVibrate = shouldVibrate;
}
- public boolean onEnrollResult(BiometricAuthenticator.Identifier identifier,
- int remaining) {
- if (remaining == 0) {
- mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier);
- logOnEnrolled(getTargetUserId(),
- System.currentTimeMillis() - mEnrollmentStartTimeMs,
- true /* enrollSuccessful */);
- }
- notifyUserActivity();
- return sendEnrollResult(identifier, remaining);
- }
-
- /*
- * @return true if we're done.
- */
- private boolean sendEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
+ public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
if (mShouldVibrate) {
vibrateSuccess();
}
+ final ClientMonitorCallbackConverter listener = getListener();
try {
- final ClientMonitorCallbackConverter listener = getListener();
if (listener != null) {
listener.onEnrollResult(identifier, remaining);
}
- return remaining == 0;
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify EnrollResult:", e);
- return true;
+ Slog.e(TAG, "Remote exception", e);
}
+
+ if (remaining == 0) {
+ mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier);
+ logOnEnrolled(getTargetUserId(), System.currentTimeMillis() - mEnrollmentStartTimeMs,
+ true /* enrollSuccessful */);
+ mFinishCallback.onClientFinished(this, true /* success */);
+ }
+ notifyUserActivity();
}
@Override
- public void start() {
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(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..dad5cad 100644
--- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
@@ -16,38 +16,47 @@
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,
- 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);
+ public GenerateChallengeClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
+ @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, 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 FinishCallback finishCallback) {
+ super.start(finishCallback);
+
startHalOperation();
try {
getListener().onChallengeGenerated(mChallenge);
+ mFinishCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
- mFinishCallback.onClientFinished(this);
- }
-
- @Override
- protected void stopHalOperation() {
- // Not supported for GenerateChallenge
}
}
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..6d7b0fd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -27,6 +27,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* Wraps {@link InternalEnumerateClient} and {@link RemovalClient}. Keeps track of all the
@@ -37,8 +38,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<S extends BiometricAuthenticator.Identifier, T>
+ extends ClientMonitor<T> implements EnumerateConsumer, RemovalConsumer {
private static final String TAG = "Biometrics/InternalCleanupClient";
@@ -57,74 +58,80 @@
private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>();
private final BiometricUtils mBiometricUtils;
- private final List<? extends BiometricAuthenticator.Identifier> mEnrolledList;
- private ClientMonitor mCurrentTask;
+ private final Map<Integer, Long> mAuthenticatorIds;
+ private final List<S> mEnrolledList;
+ private ClientMonitor<T> mCurrentTask;
- private final FinishCallback mEnumerateFinishCallback = clientMonitor -> {
+ private final FinishCallback mEnumerateFinishCallback = (clientMonitor, success) -> {
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()));
}
if (mUnknownHALTemplates.isEmpty()) {
// No unknown HAL templates. Unknown framework templates are already cleaned up in
// InternalEnumerateClient. Finish this client.
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, success);
} else {
startCleanupUnknownHalTemplates();
}
};
- private final FinishCallback mRemoveFinishCallback = clientMonitor -> {
- mFinishCallback.onClientFinished(this);
+ private final FinishCallback mRemoveFinishCallback = (clientMonitor, success) -> {
+ mFinishCallback.onClientFinished(this, success);
};
- protected abstract InternalEnumerateClient getEnumerateClient(FinishCallback finishCallback,
- Context context, IBinder token, int userId, boolean restricted, String owner,
- List<? extends BiometricAuthenticator.Identifier> enrolledList, BiometricUtils utils,
- int sensorId, int statsModality);
+ protected abstract InternalEnumerateClient<T> getEnumerateClient(Context context,
+ LazyDaemon<T> lazyDaemon, IBinder token, int userId, String owner,
+ List<S> enrolledList, BiometricUtils utils, 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, LazyDaemon<T> lazyDaemon,
+ IBinder token, int biometricId, int userId, String owner, BiometricUtils utils,
+ int sensorId, Map<Integer, Long> authenticatorIds);
- protected InternalCleanupClient(@NonNull FinishCallback finishCallback,
- @NonNull Context context, int userId, boolean restricted,
- @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,
+ protected InternalCleanupClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ int userId, @NonNull String owner, int sensorId, int statsModality,
+ @NonNull List<S> enrolledList, @NonNull BiometricUtils utils,
+ @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, null /* token */, null /* ClientMonitorCallbackConverter */,
+ userId, owner, 0 /* cookie */, sensorId, statsModality,
BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN);
mBiometricUtils = utils;
+ mAuthenticatorIds = authenticatorIds;
mEnrolledList = enrolledList;
}
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(), mLazyDaemon, getToken(),
+ template.mIdentifier.getBiometricId(), template.mUserId,
+ getContext().getPackageName(), mBiometricUtils, getSensorId(), mAuthenticatorIds);
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
mStatsModality,
BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL);
- mCurrentTask.start();
+ mCurrentTask.start(mRemoveFinishCallback);
}
@Override
- public void start() {
+ public void unableToStart() {
+ // nothing to do here
+ }
+
+ @Override
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(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(), mLazyDaemon, getToken(), getTargetUserId(),
+ getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId());
+ mCurrentTask.start(mEnumerateFinishCallback);
}
@Override
@@ -134,11 +141,6 @@
}
@Override
- protected void stopHalOperation() {
- // Internal cleanup's cannot be stopped.
- }
-
- @Override
public void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
if (!(mCurrentTask instanceof RemovalClient)) {
Slog.e(TAG, "onRemoved received during client: "
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..3f73cd5 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,
- @NonNull String owner,
+ protected InternalEnumerateClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ @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, lazyDaemon, token, null /* ClientMonitorCallbackConverter */, userId, owner,
+ 0 /* cookie */, sensorId, statsModality, BiometricsProtoEnums.ACTION_ENUMERATE,
BiometricsProtoEnums.CLIENT_UNKNOWN);
mEnrolledList = enrolledList;
mUtils = utils;
@@ -63,12 +62,19 @@
handleEnumeratedTemplate(identifier);
if (remaining == 0) {
doTemplateCleanup();
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, true /* success */);
}
}
@Override
- public void start() {
+ public void unableToStart() {
+ // Nothing to do here
+ }
+
+ @Override
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(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/LockoutResetTracker.java b/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java
new file mode 100644
index 0000000..9bb5c6e
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java
@@ -0,0 +1,122 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.ArrayList;
+
+/**
+ * Allows clients (such as keyguard) to register for notifications on when biometric lockout
+ * ends.
+ */
+public class LockoutResetTracker implements IBinder.DeathRecipient {
+
+ private static final String TAG = "LockoutResetTracker";
+
+ private final Context mContext;
+ private final ArrayList<ClientCallback> mClientCallbacks;
+
+ private static class ClientCallback {
+ private static final long WAKELOCK_TIMEOUT_MS = 2000;
+
+ private final String mOpPackageName;
+ private final IBiometricServiceLockoutResetCallback mCallback;
+ private final PowerManager.WakeLock mWakeLock;
+
+ ClientCallback(Context context, IBiometricServiceLockoutResetCallback callback,
+ String opPackageName) {
+ final PowerManager pm = context.getSystemService(PowerManager.class);
+ mOpPackageName = opPackageName;
+ mCallback = callback;
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "LockoutResetMonitor:SendLockoutReset");
+ }
+
+ void sendLockoutReset() {
+ if (mCallback != null) {
+ try {
+ mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
+ mCallback.onLockoutReset(new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) {
+ releaseWakelock();
+ }
+ });
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to invoke onLockoutReset: ", e);
+ releaseWakelock();
+ }
+ }
+ }
+
+ private void releaseWakelock() {
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ }
+ }
+
+ public LockoutResetTracker(Context context) {
+ mContext = context;
+ mClientCallbacks = new ArrayList<>();
+ }
+
+ public void addCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName) {
+ if (callback == null) {
+ Slog.w(TAG, "Callback from : " + opPackageName + " is null");
+ return;
+ }
+
+ mClientCallbacks.add(new ClientCallback(mContext, callback, opPackageName));
+ try {
+ callback.asBinder().linkToDeath(this, 0 /* flags */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to link to death", e);
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ // Do nothing, handled below
+ }
+
+ @Override
+ public void binderDied(IBinder who) {
+ Slog.e(TAG, "Callback binder died: " + who);
+ for (ClientCallback callback : mClientCallbacks) {
+ if (callback.mCallback.asBinder().equals(who)) {
+ Slog.e(TAG, "Removing dead callback for: " + callback.mOpPackageName);
+ callback.releaseWakelock();
+ mClientCallbacks.remove(callback);
+ }
+ }
+ }
+
+ public void notifyLockoutResetCallbacks() {
+ for (ClientCallback callback : mClientCallbacks) {
+ callback.sendLockoutReset();
+ }
+ }
+}
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..1c49bcdb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -24,29 +24,40 @@
import android.os.RemoteException;
import android.util.Slog;
+import java.util.Map;
+
/**
* 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;
+ private final Map<Integer, Long> mAuthenticatorIds;
- public RemovalClient(@NonNull FinishCallback finishCallback, @NonNull Context context,
+ public RemovalClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
@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,
+ int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils utils,
+ int sensorId, @NonNull Map<Integer, Long> authenticatorIds, int statsModality) {
+ super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
+ statsModality, BiometricsProtoEnums.ACTION_REMOVE,
BiometricsProtoEnums.CLIENT_UNKNOWN);
mBiometricId = biometricId;
mBiometricUtils = utils;
+ mAuthenticatorIds = authenticatorIds;
}
@Override
- public void start() {
+ public void unableToStart() {
+ // Nothing to do here
+ }
+
+ @Override
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
+
// The biometric template ids will be removed when we get confirmation from the HAL
startHalOperation();
}
@@ -67,7 +78,14 @@
}
if (remaining == 0) {
- mFinishCallback.onClientFinished(this);
+ if (mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId()).isEmpty()) {
+ Slog.d(TAG, "Last biometric removed for user: " + getTargetUserId());
+ // When the last biometric of a group is removed, update the authenticator id.
+ // Note that multiple ClientMonitors may be cause onRemoved (e.g. internal
+ // cleanup).
+ mAuthenticatorIds.put(getTargetUserId(), 0L);
+ }
+ mFinishCallback.onClientFinished(this, true /* success */);
}
}
}
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..b78ee49 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
@@ -16,28 +16,30 @@
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,
- BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN);
+ public RevokeChallengeClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
+ @NonNull IBinder token, @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, 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 FinishCallback finishCallback) {
+ super.start(finishCallback);
+
startHalOperation();
- mFinishCallback.onClientFinished(this);
- }
-
- @Override
- protected void stopHalOperation() {
- // Not supported for RevokeChallenge
+ mFinishCallback.onClientFinished(this, true /* success */);
}
}
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..118cadc 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,18 @@
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 LazyDaemon<IBiometricsFace> lazyDaemon, @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, lazyDaemon, 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,36 +88,24 @@
}
@Override
- public void start() {
- mStarted = true;
- super.start();
- }
-
- @Override
- public void cancel() {
- mStarted = false;
- super.cancel();
- }
-
- @Override
protected void startHalOperation() {
try {
- mDaemon.authenticate(mOperationId);
+ getFreshDaemon().authenticate(mOperationId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting auth", e);
onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
@Override
protected void stopHalOperation() {
try {
- mDaemon.cancel();
+ getFreshDaemon().cancel();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting cancel", e);
onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
@@ -139,10 +121,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 */,
@@ -155,7 +133,7 @@
// 1) Authenticated == true
// 2) Error occurred
// 3) Authenticated == false
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, true /* success */);
}
@Override
@@ -176,7 +154,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..4f9f46a 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,23 @@
* 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 LazyDaemon<IBiometricsFace> lazyDaemon,
+ @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, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
+ timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
+ false /* shouldVibrate */);
mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
mSurfaceHandle = surfaceHandle;
mEnrollIgnoreList = getContext().getResources()
@@ -88,36 +90,36 @@
}
android.hardware.biometrics.face.V1_1.IBiometricsFace daemon11 =
- android.hardware.biometrics.face.V1_1.IBiometricsFace.castFrom(mDaemon);
+ android.hardware.biometrics.face.V1_1.IBiometricsFace.castFrom(getFreshDaemon());
try {
final int status;
if (daemon11 != null) {
status = daemon11.enroll_1_1(token, mTimeoutSec, disabledFeatures, mSurfaceHandle);
} else if (mSurfaceHandle == null) {
- status = mDaemon.enroll(token, mTimeoutSec, disabledFeatures);
+ status = getFreshDaemon().enroll(token, mTimeoutSec, disabledFeatures);
} else {
Slog.e(TAG, "enroll(): surface is only supported in @1.1 HAL");
status = BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS;
}
if (status != Status.OK) {
onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting enroll", e);
onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
@Override
protected void stopHalOperation() {
try {
- mDaemon.cancel();
+ getFreshDaemon().cancel();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting cancel", e);
onError(BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
}
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..67f2712 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,24 +31,21 @@
* {@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 LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) {
- super(finishCallback, context, token, listener, owner, sensorId);
- mDaemon = daemon;
+ super(context, lazyDaemon, token, listener, owner, sensorId);
}
@Override
protected void startHalOperation() {
try {
- mChallenge = mDaemon.generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
+ mChallenge = getFreshDaemon().generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
} catch (RemoteException e) {
Slog.e(TAG, "generateChallenge failed", e);
}
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..ce57cb7
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java
@@ -0,0 +1,80 @@
+/*
+ * 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 LazyDaemon<IBiometricsFace> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
+ @NonNull String owner, int sensorId, int feature, int faceId) {
+ super(context, lazyDaemon, 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 FinishCallback finishCallback) {
+ super.start(finishCallback);
+ startHalOperation();
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ final OptionalBool result = getFreshDaemon().getFeature(mFeature, mFaceId);
+ getListener().onFeatureGet(result.status == Status.OK, mFeature, result.value);
+ mFinishCallback.onClientFinished(this, true /* success */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to getFeature", e);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+}
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..93f35f4 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
@@ -18,8 +18,9 @@
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.hardware.face.Face;
import android.os.IBinder;
import com.android.server.biometrics.sensors.BiometricUtils;
@@ -28,42 +29,40 @@
import com.android.server.biometrics.sensors.RemovalClient;
import java.util.List;
+import java.util.Map;
/**
* Face-specific internal cleanup client supporting the
* {@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<Face, 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,
- @NonNull BiometricUtils utils) {
- super(finishCallback, context, userId, restricted, owner, sensorId, statsModality,
- enrolledList, utils);
- mDaemon = daemon;
+ FaceInternalCleanupClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner,
+ int sensorId, @NonNull List<Face> enrolledList, @NonNull BiometricUtils utils,
+ @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FACE,
+ enrolledList, utils, authenticatorIds);
}
@Override
- protected InternalEnumerateClient getEnumerateClient(FinishCallback finishCallback,
- Context context, IBinder token, int userId, boolean restricted, 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);
+ protected InternalEnumerateClient<IBiometricsFace> getEnumerateClient(Context context,
+ LazyDaemon<IBiometricsFace> lazyDaemon, IBinder token, int userId, String owner,
+ List<Face> enrolledList, BiometricUtils utils, int sensorId) {
+ return new FaceInternalEnumerateClient(context, lazyDaemon, 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,
+ LazyDaemon<IBiometricsFace> lazyDaemon, IBinder token,
+ int biometricId, int userId, String owner, BiometricUtils utils, int sensorId,
+ Map<Integer, Long> authenticatorIds) {
// 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, lazyDaemon, token,
+ null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils,
+ sensorId, authenticatorIds);
}
}
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..f25242e 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
@@ -18,8 +18,9 @@
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.hardware.face.Face;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
@@ -34,38 +35,24 @@
* {@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,
- @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;
+ FaceInternalEnumerateClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token, int userId,
+ @NonNull String owner, @NonNull List<Face> enrolledList, @NonNull BiometricUtils utils,
+ int sensorId) {
+ super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
+ BiometricsProtoEnums.MODALITY_FACE);
}
@Override
protected void startHalOperation() {
try {
- mDaemon.enumerate();
+ getFreshDaemon().enumerate();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting enumerate", e);
- mFinishCallback.onClientFinished(this);
- }
- }
-
- @Override
- protected void stopHalOperation() {
- try {
- mDaemon.cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting cancel", e);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
}
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..00d5f50 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;
@@ -27,41 +28,30 @@
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.RemovalClient;
+import java.util.Map;
+
/**
* 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,
- @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;
+ FaceRemovalClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
+ int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils utils,
+ int sensorId, @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
+ authenticatorIds, BiometricsProtoEnums.MODALITY_FACE);
}
@Override
protected void startHalOperation() {
try {
- mDaemon.remove(mBiometricId);
+ getFreshDaemon().remove(mBiometricId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting remove", e);
- mFinishCallback.onClientFinished(this);
- }
- }
-
- @Override
- protected void stopHalOperation() {
- try {
- mDaemon.cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting cancel", e);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
}
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..69070da
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java
@@ -0,0 +1,74 @@
+/*
+ * 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,
+ @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, String owner, int sensorId,
+ @NonNull byte[] hardwareAuthToken) {
+ super(context, lazyDaemon, 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 FinishCallback finishCallback) {
+ super.start(finishCallback);
+ startHalOperation();
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ getFreshDaemon().resetLockout(mHardwareAuthToken);
+ mFinishCallback.onClientFinished(this, true /* success */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to reset lockout", e);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+}
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..a10c573 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,23 +29,20 @@
* 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 LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
+ @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, owner, sensorId);
}
@Override
protected void startHalOperation() {
try {
- mDaemon.revokeChallenge();
+ getFreshDaemon().revokeChallenge();
} catch (RemoteException e) {
Slog.e(TAG, "revokeChallenge failed", e);
}
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..a7f8220 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
@@ -25,15 +25,12 @@
import android.app.NotificationManager;
import android.content.Context;
import android.content.pm.UserInfo;
-import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
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;
@@ -62,6 +59,7 @@
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.EnrollClient;
import com.android.server.biometrics.sensors.GenerateChallengeClient;
+import com.android.server.biometrics.sensors.LockoutResetTracker;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.RemovalClient;
@@ -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;
@@ -99,6 +97,9 @@
static final String NOTIFICATION_TAG = "FaceService";
static final int NOTIFICATION_ID = 1;
+ private final LockoutResetTracker mLockoutResetTracker;
+ private final ClientMonitor.LazyDaemon<IBiometricsFace> mLazyDaemon;
+
/**
* Receives the incoming binder calls from FaceManager.
*/
@@ -111,19 +112,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(),
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), opPackageName,
+ getSensorId());
generateChallengeInternal(client);
}
@@ -131,14 +125,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(),
+ mLazyDaemon, token, owner, getSensorId());
// TODO(b/137106905): Schedule binder calls in FaceService to avoid deadlocks.
if (getCurrentClient() == null) {
@@ -153,37 +141,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(), mLazyDaemon, 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 +176,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(),
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, opId,
+ restricted, opPackageName, 0 /* cookie */, false /* requireConfirmation */,
+ getSensorId(), isStrongBiometric(), statsClient, mTaskStackListener,
+ mLockoutTracker, mUsageStats);
authenticateInternal(client, opPackageName);
}
@@ -226,25 +195,16 @@
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(),
- BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, mTaskStackListener,
- mLockoutTracker, mUsageStats);
+ final AuthenticationClient client = new FaceAuthenticationClient(getContext(),
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(sensorReceiver), userId,
+ opId, restricted, opPackageName, cookie, requireConfirmation, getSensorId(),
+ isStrongBiometric(), BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
+ mTaskStackListener, mLockoutTracker, mUsageStats);
authenticateInternal(client, opPackageName, callingUid, callingPid,
callingUserId);
}
@@ -272,36 +232,28 @@
@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(), mLazyDaemon, token,
+ new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName,
+ getBiometricUtils(), getSensorId(), mAuthenticatorIds);
removeInternal(client);
}
@Override
- public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback)
- throws RemoteException {
+ public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback,
+ final String opPackageName) {
checkPermission(USE_BIOMETRIC_INTERNAL);
- FaceService.super.addLockoutResetCallback(callback);
+ mHandler.post(() -> {
+ mLockoutResetTracker.addCallback(callback, opPackageName);
+ });
}
@Override // Binder call
@@ -369,6 +321,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 +344,50 @@
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(),
+ mLazyDaemon, 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(),
+ mLazyDaemon, 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 +398,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(),
+ mLazyDaemon, 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);
@@ -620,7 +546,7 @@
mHandler.post(() -> {
if (duration == 0) {
- notifyLockoutResetMonitors();
+ mLockoutResetTracker.notifyLockoutResetCallbacks();
}
});
}
@@ -628,6 +554,8 @@
public FaceService(Context context) {
super(context);
+ mLazyDaemon = FaceService.this::getFaceDaemon;
+ mLockoutResetTracker = new LockoutResetTracker(context);
mLockoutTracker = new LockoutHalImpl();
mUsageStats = new UsageStats(context);
mNotificationManager = getContext().getSystemService(NotificationManager.class);
@@ -658,6 +586,11 @@
}
@Override
+ protected IBiometricsFace getDaemon() {
+ return getFaceDaemon();
+ }
+
+ @Override
protected BiometricUtils getBiometricUtils() {
return FaceUtils.getInstance();
}
@@ -683,12 +616,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);
@@ -750,7 +682,7 @@
@Override
protected List<Face> getEnrolledTemplates(int userId) {
- return getBiometricUtils().getBiometricsForUser(getContext(), userId);
+ return FaceUtils.getInstance().getBiometricsForUser(getContext(), userId);
}
@Override
@@ -770,19 +702,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 List<Face> enrolledList = getEnrolledTemplates(userId);
+ final FaceInternalCleanupClient client = new FaceInternalCleanupClient(getContext(),
+ mLazyDaemon, userId, getContext().getOpPackageName(), getSensorId(), enrolledList,
+ getBiometricUtils(), mAuthenticatorIds);
cleanupInternal(client);
}
@@ -816,7 +739,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..e7d041a
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java
@@ -0,0 +1,91 @@
+/*
+ * 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 LazyDaemon<IBiometricsFace> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
+ @NonNull String owner, int sensorId, int feature, boolean enabled,
+ byte[] hardwareAuthToken, int faceId) {
+ super(context, lazyDaemon, 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 FinishCallback finishCallback) {
+ super.start(finishCallback);
+
+ startHalOperation();
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ final int result = getFreshDaemon()
+ .setFeature(mFeature, mEnabled, mHardwareAuthToken, mFaceId);
+ getListener().onFeatureSet(result == Status.OK, mFeature);
+ mFinishCallback.onClientFinished(this, true /* success */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to set feature: " + mFeature + " to enabled: " + mEnabled, e);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+}
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..b01b856 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,23 @@
* {@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 LazyDaemon<IBiometricsFingerprint> lazyDaemon, @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, lazyDaemon, token, listener, targetUserId, operationId, restricted,
owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener,
lockoutTracker);
- mDaemon = daemon;
mLockoutFrameworkImpl = lockoutTracker;
}
@@ -76,7 +73,7 @@
if (authenticated) {
resetFailedAttempts(getTargetUserId());
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, true /* success */);
} else {
final @LockoutTracker.LockoutMode int lockoutMode =
mLockoutFrameworkImpl.getLockoutModeForUser(getTargetUserId());
@@ -87,7 +84,7 @@
? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
: BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
onError(errorCode, 0 /* vendorCode */);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, true /* success */);
}
}
}
@@ -106,24 +103,24 @@
protected void startHalOperation() {
try {
// GroupId was never used. In fact, groupId is always the same as userId.
- mDaemon.authenticate(mOperationId, getTargetUserId());
+ getFreshDaemon().authenticate(mOperationId, getTargetUserId());
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting auth", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
@Override
protected void stopHalOperation() {
try {
- mDaemon.cancel();
+ getFreshDaemon().cancel();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting cancel", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
}
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..1d56882 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,44 +34,42 @@
* {@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 LazyDaemon<IBiometricsFingerprint> lazyDaemon, @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, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
+ timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
+ true /* shouldVibrate */);
}
@Override
protected void startHalOperation() {
try {
// GroupId was never used. In fact, groupId is always the same as userId.
- mDaemon.enroll(mHardwareAuthToken, getTargetUserId(), mTimeoutSec);
+ getFreshDaemon().enroll(mHardwareAuthToken, getTargetUserId(), mTimeoutSec);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting enroll", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
@Override
protected void stopHalOperation() {
try {
- mDaemon.cancel();
+ getFreshDaemon().cancel();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting cancel", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
}
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..8fb8c99 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,24 +31,21 @@
* {@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 LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, listener, owner, sensorId);
}
@Override
protected void startHalOperation() {
try {
- mChallenge = mDaemon.preEnroll();
+ mChallenge = getFreshDaemon().preEnroll();
} catch (RemoteException e) {
Slog.e(TAG, "preEnroll failed", e);
}
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..1d72c2e 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,7 +19,9 @@
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.hardware.fingerprint.Fingerprint;
import android.os.IBinder;
import com.android.server.biometrics.sensors.BiometricUtils;
@@ -28,42 +30,43 @@
import com.android.server.biometrics.sensors.RemovalClient;
import java.util.List;
+import java.util.Map;
/**
* Fingerprint-specific internal cleanup client supporting the
* {@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<Fingerprint, 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,
- @NonNull BiometricUtils utils) {
- super(finishCallback, context, userId, restricted, owner, sensorId, statsModality,
- enrolledList, utils);
- mDaemon = daemon;
+ FingerprintInternalCleanupClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, int userId,
+ @NonNull String owner, int sensorId, @NonNull List<Fingerprint> enrolledList,
+ @NonNull BiometricUtils utils, @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, userId, owner, sensorId,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT, enrolledList, utils, authenticatorIds);
}
@Override
- protected InternalEnumerateClient getEnumerateClient(FinishCallback finishCallback,
- Context context, IBinder token, int userId, boolean restricted, 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);
+ protected InternalEnumerateClient<IBiometricsFingerprint> getEnumerateClient(
+ Context context, LazyDaemon<IBiometricsFingerprint> lazyDaemon, IBinder token,
+ int userId, String owner,
+ List<Fingerprint> enrolledList, BiometricUtils utils,
+ int sensorId) {
+ return new FingerprintInternalEnumerateClient(context, lazyDaemon, 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,
+ LazyDaemon<IBiometricsFingerprint> lazyDaemon, IBinder token,
+ int biometricId, int userId, String owner, BiometricUtils utils, int sensorId,
+ Map<Integer, Long> authenticatorIds) {
// 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, lazyDaemon, token,
+ null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils,
+ sensorId, authenticatorIds);
}
}
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..240c3c5 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
@@ -18,8 +18,9 @@
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.hardware.fingerprint.Fingerprint;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
@@ -34,38 +35,24 @@
* {@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,
- @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;
+ FingerprintInternalEnumerateClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
+ int userId, @NonNull String owner, @NonNull List<Fingerprint> enrolledList,
+ @NonNull BiometricUtils utils, int sensorId) {
+ super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT);
}
@Override
protected void startHalOperation() {
try {
- mDaemon.enumerate();
+ getFreshDaemon().enumerate();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting enumerate", e);
- mFinishCallback.onClientFinished(this);
- }
- }
-
- @Override
- protected void stopHalOperation() {
- try {
- mDaemon.cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting cancel", e);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
}
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..a9336ef 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;
@@ -27,44 +28,33 @@
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.RemovalClient;
+import java.util.Map;
+
/**
* Fingerprint-specific removal client supporting the
* {@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 LazyDaemon<IBiometricsFingerprint> lazyDaemon, @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,
+ @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
+ authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT);
}
@Override
protected void startHalOperation() {
try {
// GroupId was never used. In fact, groupId is always the same as userId.
- mDaemon.remove(getTargetUserId(), mBiometricId);
+ getFreshDaemon().remove(getTargetUserId(), mBiometricId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting remove", e);
- mFinishCallback.onClientFinished(this);
- }
- }
-
- @Override
- protected void stopHalOperation() {
- try {
- mDaemon.cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when requesting cancel", e);
- mFinishCallback.onClientFinished(this);
+ mFinishCallback.onClientFinished(this, false /* success */);
}
}
}
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..882660e 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,23 +30,21 @@
* {@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 LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
+ @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, owner, sensorId);
}
@Override
protected void startHalOperation() {
try {
- mDaemon.postEnroll();
+ getFreshDaemon().postEnroll();
} catch (RemoteException e) {
Slog.e(TAG, "revokeChallenge/postEnroll failed", e);
}
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 ba3d76f..b05400a 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
@@ -29,7 +29,6 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
-import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
@@ -64,9 +63,11 @@
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BiometricServiceBase;
import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitor;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.EnrollClient;
import com.android.server.biometrics.sensors.GenerateChallengeClient;
+import com.android.server.biometrics.sensors.LockoutResetTracker;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.RemovalClient;
@@ -82,7 +83,6 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
/**
* A service to manage multiple clients that want to access the fingerprint HAL API.
@@ -91,12 +91,16 @@
*
* @hide
*/
-public class FingerprintService extends BiometricServiceBase {
+public class FingerprintService extends BiometricServiceBase<IBiometricsFingerprint> {
protected static final String TAG = "FingerprintService";
private static final boolean DEBUG = true;
private static final String FP_DATA_DIR = "fpdata";
+ private final LockoutResetTracker mLockoutResetTracker;
+ private final ClientMonitor.LazyDaemon<IBiometricsFingerprint> mLazyDaemon;
+ private final GestureAvailabilityTracker mGestureAvailabilityTracker;
+
/**
* Receives the incoming binder calls from FingerprintManager.
*/
@@ -109,19 +113,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(), mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver),
+ opPackageName, getSensorId());
generateChallengeInternal(client);
}
@@ -129,37 +126,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(),
+ mLazyDaemon, 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(), mLazyDaemon,
+ token, new ClientMonitorCallbackConverter(receiver), userId, cryptoToken,
+ opPackageName, getBiometricUtils(), ENROLL_TIMEOUT_SEC, getSensorId());
enrollInternal(client, userId);
}
@@ -172,17 +153,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();
@@ -195,11 +168,11 @@
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,
- opPackageName, 0 /* cookie */, false /* requireConfirmation */, getSensorId(),
- isStrongBiometric, surface, statsClient, mTaskStackListener, mLockoutTracker);
+ final AuthenticationClient client = new FingerprintAuthenticationClient(getContext(),
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, opId,
+ restricted, opPackageName, 0 /* cookie */, false /* requireConfirmation */,
+ getSensorId(), isStrongBiometric, surface, statsClient, mTaskStackListener,
+ mLockoutTracker);
authenticateInternal(client, opPackageName);
}
@@ -207,24 +180,14 @@
public void prepareForAuthentication(IBinder token, long opId, int userId,
IBiometricSensorReceiver sensorReceiver, String opPackageName,
int cookie, int callingUid, int callingPid, int callingUserId,
- Surface surface) throws RemoteException {
+ Surface surface) {
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,
- restricted, opPackageName, cookie, false /* requireConfirmation */,
+ final AuthenticationClient client = new FingerprintAuthenticationClient(getContext(),
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(sensorReceiver), userId,
+ opId, restricted, opPackageName, cookie, false /* requireConfirmation */,
getSensorId(), isStrongBiometric(), surface,
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, mTaskStackListener,
mLockoutTracker);
@@ -258,34 +221,26 @@
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(), mLazyDaemon,
+ token, new ClientMonitorCallbackConverter(receiver), fingerId, userId,
+ opPackageName, getBiometricUtils(), getSensorId(), mAuthenticatorIds);
removeInternal(client);
}
@Override
- public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback)
- throws RemoteException {
+ public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback,
+ final String opPackageName) {
checkPermission(USE_BIOMETRIC_INTERNAL);
- FingerprintService.super.addLockoutResetCallback(callback);
+ mHandler.post(() -> {
+ mLockoutResetTracker.addCallback(callback, opPackageName);
+ });
}
@Override // Binder call
@@ -366,6 +321,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);
@@ -396,13 +357,13 @@
@Override
public void addClientActiveCallback(IFingerprintClientActiveCallback callback) {
checkPermission(MANAGE_FINGERPRINT);
- mClientActiveCallbacks.add(callback);
+ mGestureAvailabilityTracker.registerCallback(callback);
}
@Override
public void removeClientActiveCallback(IFingerprintClientActiveCallback callback) {
checkPermission(MANAGE_FINGERPRINT);
- mClientActiveCallbacks.remove(callback);
+ mGestureAvailabilityTracker.removeCallback(callback);
}
@Override // Binder call
@@ -510,17 +471,11 @@
}
private final LockoutFrameworkImpl mLockoutTracker;
- private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
- new CopyOnWriteArrayList<>();
private IUdfpsOverlayController mUdfpsOverlayController;
@GuardedBy("this")
private IBiometricsFingerprint mDaemon;
- private final LockoutFrameworkImpl.LockoutResetCallback mLockoutResetCallback = userId -> {
- notifyLockoutResetMonitors();
- };
-
/**
* Receives callbacks from the HAL.
*/
@@ -596,7 +551,13 @@
public FingerprintService(Context context) {
super(context);
- mLockoutTracker = new LockoutFrameworkImpl(context, mLockoutResetCallback);
+ mLazyDaemon = FingerprintService.this::getFingerprintDaemon;
+ mGestureAvailabilityTracker = new GestureAvailabilityTracker();
+ mLockoutResetTracker = new LockoutResetTracker(context);
+ final LockoutFrameworkImpl.LockoutResetCallback lockoutResetCallback = userId -> {
+ mLockoutResetTracker.notifyLockoutResetCallbacks();
+ };
+ mLockoutTracker = new LockoutFrameworkImpl(context, lockoutResetCallback);
}
@Override
@@ -612,6 +573,11 @@
}
@Override
+ protected IBiometricsFingerprint getDaemon() {
+ return getFingerprintDaemon();
+ }
+
+ @Override
protected BiometricUtils getBiometricUtils() {
return FingerprintUtils.getInstance();
}
@@ -635,12 +601,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) {
@@ -719,20 +684,12 @@
if (userId != UserHandle.getCallingUserId()) {
checkPermission(INTERACT_ACROSS_USERS);
}
- return getBiometricUtils().getBiometricsForUser(getContext(), userId);
+ return FingerprintUtils.getInstance().getBiometricsForUser(getContext(), userId);
}
@Override
protected void notifyClientActiveCallbacks(boolean isActive) {
- List<IFingerprintClientActiveCallback> callbacks = mClientActiveCallbacks;
- for (int i = 0; i < callbacks.size(); i++) {
- try {
- callbacks.get(i).onClientActiveChanged(isActive);
- } catch (RemoteException re) {
- // If the remote is dead, stop notifying it
- mClientActiveCallbacks.remove(callbacks.get(i));
- }
- }
+ mGestureAvailabilityTracker.notifyClientActiveCallbacks(isActive);
}
@Override
@@ -747,19 +704,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 List<Fingerprint> enrolledList = getEnrolledTemplates(userId);
final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(
- mClientFinishCallback, getContext(), daemon, userId, restricted,
- getContext().getOpPackageName(), getSensorId(), statsModality(), enrolledList,
- getBiometricUtils());
+ getContext(), mLazyDaemon, userId, getContext().getOpPackageName(), getSensorId(),
+ enrolledList, getBiometricUtils(), mAuthenticatorIds);
cleanupInternal(client);
}
@@ -793,7 +741,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/fingerprint/GestureAvailabilityTracker.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java
new file mode 100644
index 0000000..6292ecf
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java
@@ -0,0 +1,50 @@
+/*
+ * 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.fingerprint;
+
+import android.hardware.fingerprint.IFingerprintClientActiveCallback;
+import android.os.RemoteException;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Keeps track of sensor gesture availability (e.g. swipe), and notifies clients when its
+ * availability changes
+ */
+class GestureAvailabilityTracker {
+ private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
+ new CopyOnWriteArrayList<>();
+
+ void registerCallback(IFingerprintClientActiveCallback callback) {
+ mClientActiveCallbacks.add(callback);
+ }
+
+ void removeCallback(IFingerprintClientActiveCallback callback) {
+ mClientActiveCallbacks.remove(callback);
+ }
+
+ void notifyClientActiveCallbacks(boolean isActive) {
+ for (IFingerprintClientActiveCallback callback : mClientActiveCallbacks) {
+ try {
+ callback.onClientActiveChanged(isActive);
+ } catch (RemoteException re) {
+ // If the remote is dead, stop notifying it
+ mClientActiveCallbacks.remove(callback);
+ }
+ }
+ }
+}
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/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index c5aa8d5..a75a80a 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -82,6 +82,7 @@
private final PackageManager mPackageManager;
private final UserManager mUserManager;
private final INetd mNetd;
+ private final Dependencies mDeps;
// Values are User IDs.
@GuardedBy("this")
@@ -102,10 +103,30 @@
@GuardedBy("this")
private final Set<Integer> mAllApps = new HashSet<>();
- public PermissionMonitor(Context context, INetd netd) {
+ /**
+ * Dependencies of PermissionMonitor, for injection in tests.
+ */
+ @VisibleForTesting
+ public static class Dependencies {
+ /**
+ * Get device first sdk version.
+ */
+ public int getDeviceFirstSdkInt() {
+ return Build.VERSION.FIRST_SDK_INT;
+ }
+ }
+
+ public PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd) {
+ this(context, netd, new Dependencies());
+ }
+
+ @VisibleForTesting
+ PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd,
+ @NonNull final Dependencies deps) {
mPackageManager = context.getPackageManager();
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mNetd = netd;
+ mDeps = deps;
}
// Intended to be called only once at startup, after the system is ready. Installs a broadcast
@@ -186,11 +207,6 @@
}
@VisibleForTesting
- protected int getDeviceFirstSdkInt() {
- return Build.VERSION.FIRST_SDK_INT;
- }
-
- @VisibleForTesting
boolean hasPermission(@NonNull final PackageInfo app, @NonNull final String permission) {
if (app.requestedPermissions == null || app.requestedPermissionsFlags == null) {
return false;
@@ -212,7 +228,7 @@
if (app.applicationInfo != null) {
// Backward compatibility for b/114245686, on devices that launched before Q daemons
// and apps running as the system UID are exempted from this check.
- if (app.applicationInfo.uid == SYSTEM_UID && getDeviceFirstSdkInt() < VERSION_Q) {
+ if (app.applicationInfo.uid == SYSTEM_UID && mDeps.getDeviceFirstSdkInt() < VERSION_Q) {
return true;
}
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/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index 70f0399..05cf40a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -120,6 +120,11 @@
public abstract void reportImeControl(@Nullable IBinder windowToken);
/**
+ * Destroys the IME surface.
+ */
+ public abstract void removeImeSurface();
+
+ /**
* Fake implementation of {@link InputMethodManagerInternal}. All the methods do nothing.
*/
private static final InputMethodManagerInternal NOP =
@@ -166,6 +171,10 @@
@Override
public void reportImeControl(@Nullable IBinder windowToken) {
}
+
+ @Override
+ public void removeImeSurface() {
+ }
};
/**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 9acb475..d8ee32e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -211,6 +211,7 @@
static final int MSG_INITIALIZE_IME = 1040;
static final int MSG_CREATE_SESSION = 1050;
static final int MSG_REMOVE_IME_SURFACE = 1060;
+ static final int MSG_REMOVE_IME_SURFACE_FROM_WINDOW = 1061;
static final int MSG_START_INPUT = 2000;
@@ -2128,6 +2129,11 @@
public void onInputMethodFinishInput() throws RemoteException {
mCallback.onInputMethodFinishInput();
}
+
+ @Override
+ public void onInlineSuggestionsSessionInvalidated() throws RemoteException {
+ mCallback.onInlineSuggestionsSessionInvalidated();
+ }
}
/**
@@ -4000,6 +4006,13 @@
mHandler.sendMessage(mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE));
}
+ @Override
+ public void removeImeSurfaceFromWindow(IBinder windowToken) {
+ // No permission check, because we'll only execute the request if the calling window is
+ // also the current IME client.
+ mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE_FROM_WINDOW, windowToken).sendToTarget();
+ }
+
@BinderThread
private void notifyUserAction(@NonNull IBinder token) {
if (DEBUG) {
@@ -4047,7 +4060,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.
@@ -4271,11 +4286,27 @@
return true;
}
case MSG_REMOVE_IME_SURFACE: {
- try {
- if (mEnabledSession != null && mEnabledSession.session != null) {
- mEnabledSession.session.removeImeSurface();
+ synchronized (mMethodMap) {
+ try {
+ if (mEnabledSession != null && mEnabledSession.session != null
+ && !mShowRequested) {
+ mEnabledSession.session.removeImeSurface();
+ }
+ } catch (RemoteException e) {
}
- } catch (RemoteException e) {
+ }
+ return true;
+ }
+ case MSG_REMOVE_IME_SURFACE_FROM_WINDOW: {
+ IBinder windowToken = (IBinder) msg.obj;
+ synchronized (mMethodMap) {
+ try {
+ if (windowToken == mCurFocusedWindow
+ && mEnabledSession != null && mEnabledSession.session != null) {
+ mEnabledSession.session.removeImeSurface();
+ }
+ } catch (RemoteException e) {
+ }
}
return true;
}
@@ -5109,6 +5140,11 @@
public void reportImeControl(@Nullable IBinder windowToken) {
mService.reportImeControl(windowToken);
}
+
+ @Override
+ public void removeImeSurface() {
+ mService.mHandler.sendMessage(mService.mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE));
+ }
}
@BinderThread
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 23392db..2516e28 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -226,6 +226,11 @@
@Override
public void reportImeControl(@Nullable IBinder windowToken) {
}
+
+ @Override
+ public void removeImeSurface() {
+ reportNotSupported();
+ }
});
}
@@ -1482,6 +1487,12 @@
@BinderThread
@Override
+ public void removeImeSurfaceFromWindow(IBinder windowToken) {
+ reportNotSupported();
+ }
+
+ @BinderThread
+ @Override
public boolean showSoftInput(
IInputMethodClient client, IBinder token, int flags,
ResultReceiver resultReceiver) {
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 9a6ffd0..f69c823 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -20,10 +20,18 @@
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.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 +63,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 +80,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 +144,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 +173,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 +253,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 +486,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 +641,8 @@
manager = new LocationProviderManager(name);
mProviderManagers.add(manager);
}
- manager.setMockProvider(new MockProvider(properties));
+ manager.setMockProvider(
+ new MockProvider(properties, CallerIdentity.fromContext(mContext)));
}
}
@@ -1007,9 +1019,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));
@@ -1209,21 +1221,10 @@
false);
// Now update monitoring of high power requests only.
- boolean wasHighPowerMonitoring = mOpHighPowerMonitoring;
mOpHighPowerMonitoring = updateMonitoring(
requestingHighPowerLocation,
mOpHighPowerMonitoring,
true);
- if (mOpHighPowerMonitoring != wasHighPowerMonitoring) {
- 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);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
}
private boolean updateMonitoring(boolean allowMonitoring, boolean currentlyMonitoring,
@@ -1271,7 +1272,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 +1288,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 +1313,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 +1333,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 +1393,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 +2094,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 +2130,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 +2440,7 @@
mContext.enforceCallingOrSelfPermission(permission.WRITE_SECURE_SETTINGS, null);
- LocationManager.invalidateLocalLocationEnabledCaches();
+ invalidateLocalLocationEnabledCaches();
mSettingsHelper.setLocationEnabled(enabled, userId);
}
@@ -2560,8 +2565,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 +2655,7 @@
mProviderManagers.add(manager);
}
- manager.setMockProvider(new MockProvider(properties));
+ manager.setMockProvider(new MockProvider(properties, identity));
}
}
@@ -2839,6 +2842,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/gnss/GnssVisibilityControl.java b/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java
index 70d83a9..631dbbf 100644
--- a/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java
+++ b/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java
@@ -27,7 +27,6 @@
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.location.LocationManager;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
@@ -582,17 +581,9 @@
mAppOps.finishOp(AppOpsManager.OP_MONITOR_LOCATION, uid, proxyAppPkgName);
mAppOps.finishOp(AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, uid, proxyAppPkgName);
}
- sendHighPowerMonitoringBroadcast();
return true;
}
- private void sendHighPowerMonitoringBroadcast() {
- // Send an intent to notify that a high power request has been added/removed so that
- // the SystemUi checks the state of AppOps and updates the location icon accordingly.
- Intent intent = new Intent(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- }
-
private void handleEmergencyNfwNotification(NfwNotification nfwNotification) {
boolean isPermissionMismatched = false;
if (!nfwNotification.isRequestAccepted()) {
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..a953836 100644
--- a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
+++ b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
@@ -16,12 +16,8 @@
package com.android.server.location.util;
-import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
-
import android.app.AppOpsManager;
import android.content.Context;
-import android.content.Intent;
-import android.location.LocationManager;
import android.location.util.identity.CallerIdentity;
import android.os.Binder;
@@ -54,11 +50,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));
});
}
@@ -68,22 +62,13 @@
long identity = Binder.clearCallingIdentity();
try {
- boolean allowed = mAppOps.startOpNoThrow(
+ return mAppOps.startOpNoThrow(
appOp,
callerIdentity.getUid(),
callerIdentity.getPackageName(),
false,
callerIdentity.getAttributionTag(),
callerIdentity.getListenerId()) == AppOpsManager.MODE_ALLOWED;
-
- if (allowed && appOp == OP_MONITOR_HIGH_POWER_LOCATION) {
- // notify of possible location icon change
- mContext.sendBroadcast(
- new Intent(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION).addFlags(
- Intent.FLAG_RECEIVER_FOREGROUND));
- }
-
- return allowed;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -100,13 +85,6 @@
callerIdentity.getUid(),
callerIdentity.getPackageName(),
callerIdentity.getAttributionTag());
-
- if (appOp == OP_MONITOR_HIGH_POWER_LOCATION) {
- // notify of possible location icon change
- mContext.sendBroadcast(
- new Intent(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION).addFlags(
- Intent.FLAG_RECEIVER_FOREGROUND));
- }
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index 7b767b8..c4581c8 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -155,7 +155,7 @@
pw.println(" set-pin [--old <CREDENTIAL>] [--user USER_ID] <PIN>");
pw.println(" Sets the lock screen as PIN, using the given PIN to unlock.");
pw.println("");
- pw.println(" set-pin [--old <CREDENTIAL>] [--user USER_ID] <PASSWORD>");
+ pw.println(" set-password [--old <CREDENTIAL>] [--user USER_ID] <PASSWORD>");
pw.println(" Sets the lock screen as password, using the given PASSOWRD to unlock.");
pw.println("");
pw.println(" sp [--old <CREDENTIAL>] [--user USER_ID]");
diff --git a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
index 55e0795..b9822fcb 100644
--- a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
+++ b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
@@ -26,12 +26,12 @@
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.IntArray;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -104,7 +104,7 @@
// TODO(b/35278867): Find and use unique identifier for apps because apps may share the UID.
@GuardedBy("mLock")
@SuppressWarnings("WeakerAccess") /* synthetic access */
- final IntArray mSortedAudioPlaybackClientUids = new IntArray();
+ final List<Integer> mSortedAudioPlaybackClientUids = new ArrayList<>();
static AudioPlayerStateMonitor getInstance(Context context) {
synchronized (AudioPlayerStateMonitor.class) {
@@ -145,8 +145,8 @@
* audio/video) The UID whose audio is currently playing comes first, then the UID whose audio
* playback becomes active at the last comes next.
*/
- public IntArray getSortedAudioPlaybackClientUids() {
- IntArray sortedAudioPlaybackClientUids = new IntArray();
+ public List<Integer> getSortedAudioPlaybackClientUids() {
+ List<Integer> sortedAudioPlaybackClientUids = new ArrayList();
synchronized (mLock) {
sortedAudioPlaybackClientUids.addAll(mSortedAudioPlaybackClientUids);
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index dbf1abc..0f67483 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -50,7 +50,6 @@
import android.os.ResultReceiver;
import android.os.SystemClock;
import android.util.Log;
-import android.util.Slog;
import android.view.KeyEvent;
import com.android.server.LocalServices;
@@ -996,7 +995,7 @@
}
return true;
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
+ Log.e(TAG, "Remote failure in sendMediaRequest.", e);
}
return false;
}
@@ -1013,7 +1012,7 @@
}
return true;
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
+ Log.e(TAG, "Remote failure in sendMediaRequest.", e);
}
return false;
}
@@ -1023,7 +1022,7 @@
try {
mCb.onCommand(packageName, pid, uid, command, args, cb);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in sendCommand.", e);
+ Log.e(TAG, "Remote failure in sendCommand.", e);
}
}
@@ -1032,7 +1031,7 @@
try {
mCb.onCustomAction(packageName, pid, uid, action, args);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in sendCustomAction.", e);
+ Log.e(TAG, "Remote failure in sendCustomAction.", e);
}
}
@@ -1040,7 +1039,7 @@
try {
mCb.onPrepare(packageName, pid, uid);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in prepare.", e);
+ Log.e(TAG, "Remote failure in prepare.", e);
}
}
@@ -1049,7 +1048,7 @@
try {
mCb.onPrepareFromMediaId(packageName, pid, uid, mediaId, extras);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in prepareFromMediaId.", e);
+ Log.e(TAG, "Remote failure in prepareFromMediaId.", e);
}
}
@@ -1058,7 +1057,7 @@
try {
mCb.onPrepareFromSearch(packageName, pid, uid, query, extras);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in prepareFromSearch.", e);
+ Log.e(TAG, "Remote failure in prepareFromSearch.", e);
}
}
@@ -1066,7 +1065,7 @@
try {
mCb.onPrepareFromUri(packageName, pid, uid, uri, extras);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in prepareFromUri.", e);
+ Log.e(TAG, "Remote failure in prepareFromUri.", e);
}
}
@@ -1074,7 +1073,7 @@
try {
mCb.onPlay(packageName, pid, uid);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in play.", e);
+ Log.e(TAG, "Remote failure in play.", e);
}
}
@@ -1083,7 +1082,7 @@
try {
mCb.onPlayFromMediaId(packageName, pid, uid, mediaId, extras);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in playFromMediaId.", e);
+ Log.e(TAG, "Remote failure in playFromMediaId.", e);
}
}
@@ -1092,7 +1091,7 @@
try {
mCb.onPlayFromSearch(packageName, pid, uid, query, extras);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in playFromSearch.", e);
+ Log.e(TAG, "Remote failure in playFromSearch.", e);
}
}
@@ -1100,7 +1099,7 @@
try {
mCb.onPlayFromUri(packageName, pid, uid, uri, extras);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in playFromUri.", e);
+ Log.e(TAG, "Remote failure in playFromUri.", e);
}
}
@@ -1108,7 +1107,7 @@
try {
mCb.onSkipToTrack(packageName, pid, uid, id);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in skipToTrack", e);
+ Log.e(TAG, "Remote failure in skipToTrack", e);
}
}
@@ -1116,7 +1115,7 @@
try {
mCb.onPause(packageName, pid, uid);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in pause.", e);
+ Log.e(TAG, "Remote failure in pause.", e);
}
}
@@ -1124,7 +1123,7 @@
try {
mCb.onStop(packageName, pid, uid);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in stop.", e);
+ Log.e(TAG, "Remote failure in stop.", e);
}
}
@@ -1132,7 +1131,7 @@
try {
mCb.onNext(packageName, pid, uid);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in next.", e);
+ Log.e(TAG, "Remote failure in next.", e);
}
}
@@ -1140,7 +1139,7 @@
try {
mCb.onPrevious(packageName, pid, uid);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in previous.", e);
+ Log.e(TAG, "Remote failure in previous.", e);
}
}
@@ -1148,7 +1147,7 @@
try {
mCb.onFastForward(packageName, pid, uid);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in fastForward.", e);
+ Log.e(TAG, "Remote failure in fastForward.", e);
}
}
@@ -1156,7 +1155,7 @@
try {
mCb.onRewind(packageName, pid, uid);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in rewind.", e);
+ Log.e(TAG, "Remote failure in rewind.", e);
}
}
@@ -1164,7 +1163,7 @@
try {
mCb.onSeekTo(packageName, pid, uid, pos);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in seekTo.", e);
+ Log.e(TAG, "Remote failure in seekTo.", e);
}
}
@@ -1172,7 +1171,7 @@
try {
mCb.onRate(packageName, pid, uid, rating);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in rate.", e);
+ Log.e(TAG, "Remote failure in rate.", e);
}
}
@@ -1180,7 +1179,7 @@
try {
mCb.onSetPlaybackSpeed(packageName, pid, uid, speed);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in setPlaybackSpeed.", e);
+ Log.e(TAG, "Remote failure in setPlaybackSpeed.", e);
}
}
@@ -1194,7 +1193,7 @@
mCb.onAdjustVolume(packageName, pid, uid, direction);
}
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in adjustVolume.", e);
+ Log.e(TAG, "Remote failure in adjustVolume.", e);
}
}
@@ -1202,7 +1201,7 @@
try {
mCb.onSetVolumeTo(packageName, pid, uid, value);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in setVolumeTo.", e);
+ Log.e(TAG, "Remote failure in setVolumeTo.", e);
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index eba6fe6..9a83242 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -75,7 +75,6 @@
import android.speech.RecognizerIntent;
import android.text.TextUtils;
import android.util.Log;
-import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.KeyEvent;
@@ -1140,7 +1139,7 @@
}
return sessionBinder;
} catch (Exception e) {
- Slog.w(TAG, "Exception in creating a new session", e);
+ Log.w(TAG, "Exception in creating a new session", e);
throw e;
} finally {
Binder.restoreCallingIdentity(token);
@@ -1351,7 +1350,7 @@
if (!isUserSetupComplete()) {
// Global media key handling can have the side-effect of starting new
// activities which is undesirable while setup is in progress.
- Slog.i(TAG, "Not dispatching media key event because user "
+ Log.i(TAG, "Not dispatching media key event because user "
+ "setup is in progress.");
return;
}
@@ -1361,7 +1360,7 @@
if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) {
// Prevent dispatching key event through reflection while the global
// priority session is active.
- Slog.i(TAG, "Only the system can dispatch media key event "
+ Log.i(TAG, "Only the system can dispatch media key event "
+ "to the global priority session.");
return;
}
@@ -2099,7 +2098,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..953aae4 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -22,7 +22,6 @@
import android.media.session.MediaSession;
import android.os.Debug;
import android.os.UserHandle;
-import android.util.IntArray;
import android.util.Log;
import android.util.SparseArray;
@@ -190,7 +189,8 @@
if (DEBUG) {
Log.d(TAG, "updateMediaButtonSessionIfNeeded, callers=" + Debug.getCallers(2));
}
- IntArray audioPlaybackUids = mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids();
+ List<Integer> audioPlaybackUids =
+ mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids();
for (int i = 0; i < audioPlaybackUids.size(); i++) {
int audioPlaybackUid = audioPlaybackUids.get(i);
MediaSessionRecordImpl mediaButtonSession = findMediaButtonSession(audioPlaybackUid);
@@ -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/net/NetworkStatsSubscriptionsMonitor.java b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
index db553ee..5c45e67 100644
--- a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.os.Looper;
import android.telephony.Annotation;
+import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
@@ -196,7 +197,19 @@
@Override
public void onServiceStateChanged(@NonNull ServiceState ss) {
- final int networkType = ss.getDataNetworkType();
+ // In 5G SA (Stand Alone) mode, the primary cell itself will be 5G hence telephony
+ // would report RAT = 5G_NR.
+ // However, in 5G NSA (Non Stand Alone) mode, the primary cell is still LTE and
+ // network allocates a secondary 5G cell so telephony reports RAT = LTE along with
+ // NR state as connected. In such case, attributes the data usage to NR.
+ // See b/160727498.
+ final boolean is5GNsa = (ss.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE
+ || ss.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE_CA)
+ && ss.getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED;
+
+ final int networkType =
+ (is5GNsa ? TelephonyManager.NETWORK_TYPE_NR : ss.getDataNetworkType());
+
final int collapsedRatType = getCollapsedRatType(networkType);
if (collapsedRatType == mLastCollapsedRatType) return;
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..592db83 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -77,6 +77,8 @@
public static final int MATCH_ACTIVE_PACKAGE = 1 << 0;
static final int MATCH_FACTORY_PACKAGE = 1 << 1;
+ private static final String VNDK_APEX_MODULE_NAME_PREFIX = "com.android.vndk.";
+
private static final Singleton<ApexManager> sApexManagerSingleton =
new Singleton<ApexManager>() {
@Override
@@ -342,14 +344,28 @@
public abstract boolean destroyDeSnapshots(int rollbackId);
/**
+ * Deletes snapshots of the credential encrypted apex data directories for the specified user,
+ * for the given rollback id as long as the user is credential unlocked.
+ *
+ * @return boolean true if the delete was successful
+ */
+ public abstract boolean destroyCeSnapshots(int userId, int rollbackId);
+
+ /**
* Deletes snapshots of the credential encrypted apex data directories for the specified user,
- * where the rollback id is not included in {@code retainRollbackIds}.
+ * where the rollback id is not included in {@code retainRollbackIds} as long as the user is
+ * credential unlocked.
*
* @return boolean true if the delete was successful
*/
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.
@@ -521,7 +537,9 @@
activePackagesSet.add(packageInfo.packageName);
}
if (ai.isFactory) {
- if (factoryPackagesSet.contains(packageInfo.packageName)) {
+ // Don't throw when the duplicating APEX is VNDK APEX
+ if (factoryPackagesSet.contains(packageInfo.packageName)
+ && !ai.moduleName.startsWith(VNDK_APEX_MODULE_NAME_PREFIX)) {
throw new IllegalStateException(
"Two factory packages have the same name: "
+ packageInfo.packageName);
@@ -870,6 +888,17 @@
}
@Override
+ public boolean destroyCeSnapshots(int userId, int rollbackId) {
+ try {
+ waitForApexService().destroyCeSnapshots(userId, rollbackId);
+ return true;
+ } catch (Exception e) {
+ Slog.e(TAG, e.getMessage(), e);
+ return false;
+ }
+ }
+
+ @Override
public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) {
try {
waitForApexService().destroyCeSnapshotsNotSpecified(userId, retainRollbackIds);
@@ -880,6 +909,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.
@@ -1122,11 +1160,21 @@
}
@Override
+ public boolean destroyCeSnapshots(int userId, int rollbackId) {
+ return true;
+ }
+
+ @Override
public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) {
return true;
}
@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/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 3a203d5..c3c2e5e 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -708,12 +708,15 @@
return ret;
}
+ /**
+ * This method recomputes all component / intent-based visibility and is intended to match the
+ * relevant logic of {@link #addPackageInternal(PackageSetting, ArrayMap)}
+ */
private void recomputeComponentVisibility(ArrayMap<String, PackageSetting> existingSettings) {
mQueriesViaComponent.clear();
for (int i = existingSettings.size() - 1; i >= 0; i--) {
PackageSetting setting = existingSettings.valueAt(i);
- if (setting.pkg == null
- || mForceQueryable.contains(setting.appId)) {
+ if (setting.pkg == null || requestsQueryAllPackages(setting.pkg)) {
continue;
}
for (int j = existingSettings.size() - 1; j >= 0; j--) {
@@ -721,7 +724,7 @@
continue;
}
final PackageSetting otherSetting = existingSettings.valueAt(j);
- if (otherSetting.pkg == null) {
+ if (otherSetting.pkg == null || mForceQueryable.contains(otherSetting.appId)) {
continue;
}
if (canQueryViaComponents(setting.pkg, otherSetting.pkg, mProtectedBroadcasts)) {
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 2df4a92..eddab76 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -18,6 +18,7 @@
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.annotation.Nullable;
import android.content.Context;
@@ -42,10 +43,9 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
/**
* A service for A/B OTA dexopting.
@@ -123,15 +123,20 @@
}
final List<PackageSetting> important;
final List<PackageSetting> others;
+ Predicate<PackageSetting> isPlatformPackage = pkgSetting ->
+ PLATFORM_PACKAGE_NAME.equals(pkgSetting.pkg.getPackageName());
synchronized (mPackageManagerService.mLock) {
// Important: the packages we need to run with ab-ota compiler-reason.
important = PackageManagerServiceUtils.getPackagesForDexopt(
mPackageManagerService.mSettings.mPackages.values(), mPackageManagerService,
DEBUG_DEXOPT);
+ // Remove Platform Package from A/B OTA b/160735835.
+ important.removeIf(isPlatformPackage);
// Others: we should optimize this with the (first-)boot compiler-reason.
others = new ArrayList<>(mPackageManagerService.mSettings.mPackages.values());
others.removeAll(important);
others.removeIf(PackageManagerServiceUtils.REMOVE_IF_NULL_PKG);
+ others.removeIf(isPlatformPackage);
// Pre-size the array list by over-allocating by a factor of 1.5.
mDexoptCommands = new ArrayList<>(3 * mPackageManagerService.mPackages.size() / 2);
@@ -147,7 +152,7 @@
throw new IllegalStateException("Found a core app that's not important");
}
mDexoptCommands.addAll(generatePackageDexopts(pkgSetting.pkg, pkgSetting,
- PackageManagerService.REASON_FIRST_BOOT));
+ PackageManagerService.REASON_FIRST_BOOT));
}
completeSize = mDexoptCommands.size();
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelper.java b/services/core/java/com/android/server/pm/PackageAbiHelper.java
index e355bb9..30b1c2c 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelper.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelper.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Pair;
@@ -27,6 +28,8 @@
import java.io.File;
import java.util.Set;
+
+
// TODO: Move to .parsing sub-package
@VisibleForTesting
public interface PackageAbiHelper {
@@ -34,7 +37,8 @@
* Derive and get the location of native libraries for the given package,
* which varies depending on where and how the package was installed.
*/
- NativeLibraryPaths getNativeLibraryPaths(AndroidPackage pkg, PackageSetting pkgSetting,
+ @NonNull
+ NativeLibraryPaths deriveNativeLibraryPaths(AndroidPackage pkg, boolean isUpdatedSystemApp,
File appLib32InstallDir);
/**
@@ -51,7 +55,7 @@
* If {@code extractLibs} is true, native libraries are extracted from the app if required.
*/
Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg, boolean isUpdatedSystemApp,
- String cpuAbiOverride, boolean extractLibs) throws PackageManagerException;
+ String cpuAbiOverride) throws PackageManagerException;
/**
* Calculates adjusted ABIs for a set of packages belonging to a shared user so that they all
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index fc58968..8af7e1f 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -131,16 +131,16 @@
}
@Override
- public NativeLibraryPaths getNativeLibraryPaths(AndroidPackage pkg, PackageSetting pkgSetting,
- File appLib32InstallDir) {
+ public NativeLibraryPaths deriveNativeLibraryPaths(AndroidPackage pkg,
+ boolean isUpdatedSystemApp, File appLib32InstallDir) {
// Trying to derive the paths, thus need the raw ABI info from the parsed package, and the
// current state in PackageSetting is irrelevant.
- return getNativeLibraryPaths(new Abis(pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi()),
+ return deriveNativeLibraryPaths(new Abis(pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi()),
appLib32InstallDir, pkg.getCodePath(), pkg.getBaseCodePath(), pkg.isSystem(),
- pkgSetting.getPkgState().isUpdatedSystemApp());
+ isUpdatedSystemApp);
}
- private static NativeLibraryPaths getNativeLibraryPaths(final Abis abis,
+ private static NativeLibraryPaths deriveNativeLibraryPaths(final Abis abis,
final File appLib32InstallDir, final String codePath, final String sourceDir,
final boolean isSystemApp, final boolean isUpdatedSystemApp) {
final File codeFile = new File(codePath);
@@ -296,22 +296,19 @@
@Override
public Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg,
- boolean isUpdatedSystemApp, String cpuAbiOverride, boolean extractLibs)
+ boolean isUpdatedSystemApp, String cpuAbiOverride)
throws PackageManagerException {
// Give ourselves some initial paths; we'll come back for another
// pass once we've determined ABI below.
String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(pkg);
String pkgRawSecondaryCpuAbi = AndroidPackageUtils.getRawSecondaryCpuAbi(pkg);
- final NativeLibraryPaths initialLibraryPaths = getNativeLibraryPaths(
+ final NativeLibraryPaths initialLibraryPaths = deriveNativeLibraryPaths(
new Abis(pkgRawPrimaryCpuAbi, pkgRawSecondaryCpuAbi),
PackageManagerService.sAppLib32InstallDir, pkg.getCodePath(),
pkg.getBaseCodePath(), pkg.isSystem(),
isUpdatedSystemApp);
- // We shouldn't attempt to extract libs from system app when it was not updated.
- if (pkg.isSystem() && !isUpdatedSystemApp) {
- extractLibs = false;
- }
+ final boolean extractLibs = shouldExtractLibs(pkg, isUpdatedSystemApp);
final String nativeLibraryRootStr = initialLibraryPaths.nativeLibraryRootDir;
final boolean useIsaSpecificSubdirs = initialLibraryPaths.nativeLibraryRootRequiresIsa;
@@ -455,11 +452,21 @@
final Abis abis = new Abis(primaryCpuAbi, secondaryCpuAbi);
return new Pair<>(abis,
- getNativeLibraryPaths(abis, PackageManagerService.sAppLib32InstallDir,
+ deriveNativeLibraryPaths(abis, PackageManagerService.sAppLib32InstallDir,
pkg.getCodePath(), pkg.getBaseCodePath(), pkg.isSystem(),
isUpdatedSystemApp));
}
+ private boolean shouldExtractLibs(AndroidPackage pkg, boolean isUpdatedSystemApp) {
+ // We shouldn't extract libs if the package is a library or if extractNativeLibs=false
+ boolean extractLibs = !AndroidPackageUtils.isLibrary(pkg) && pkg.isExtractNativeLibs();
+ // We shouldn't attempt to extract libs from system app when it was not updated.
+ if (pkg.isSystem() && !isUpdatedSystemApp) {
+ extractLibs = false;
+ }
+ return extractLibs;
+ }
+
/**
* Adjusts ABIs for a set of packages belonging to a shared user so that they all match.
* i.e, so that all packages can be run inside a single process if required.
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 843f0ae..312dcdd 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -84,6 +84,8 @@
import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
+import com.android.server.SystemService;
+import com.android.server.SystemServiceManager;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
@@ -201,6 +203,27 @@
}
};
+ private static final class Lifecycle extends SystemService {
+ private final PackageInstallerService mPackageInstallerService;
+
+ Lifecycle(Context context, PackageInstallerService service) {
+ super(context);
+ mPackageInstallerService = service;
+ }
+
+ @Override
+ public void onStart() {
+ // no-op
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ mPackageInstallerService.onBroadcastReady();
+ }
+ }
+ }
+
public PackageInstallerService(Context context, PackageManagerService pm,
Supplier<PackageParser2> apexParserSupplier) {
mContext = context;
@@ -222,6 +245,9 @@
mApexManager = ApexManager.getInstance();
mStagingManager = new StagingManager(this, context, apexParserSupplier);
+
+ LocalServices.getService(SystemServiceManager.class).startService(
+ new Lifecycle(context, this));
}
boolean okToSendBroadcasts() {
@@ -259,6 +285,13 @@
}
}
+ private void onBroadcastReady() {
+ // Broadcasts are not sent while we restore sessions on boot, since no processes would be
+ // ready to listen to them. From now on, it is safe to send broadcasts which otherwise will
+ // be rejected by ActivityManagerService if its systemReady() is not completed.
+ mOkToSendBroadcasts = true;
+ }
+
void restoreAndApplyStagedSessionIfNeeded() {
List<PackageInstallerSession> stagedSessionsToRestore = new ArrayList<>();
synchronized (mSessions) {
@@ -281,16 +314,6 @@
}
mStagingManager.restoreSession(session, isDeviceUpgrading);
}
- // Broadcasts are not sent while we restore sessions on boot, since no processes would be
- // ready to listen to them. From now on, we greedily assume that broadcasts requests are
- // safe to send out. The worst that can happen is that a broadcast is attempted before
- // ActivityManagerService completes its own systemReady(), in which case it will be rejected
- // with an otherwise harmless exception.
- // A more appropriate way to do this would be to wait until the correct boot phase is
- // reached, but since we are not a SystemService we can't override onBootPhase.
- // Waiting on the BOOT_COMPLETED broadcast can take several minutes, so that's not a viable
- // way either.
- mOkToSendBroadcasts = true;
}
@GuardedBy("mSessions")
@@ -525,7 +548,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.
@@ -590,12 +615,12 @@
}
}
- if (mBypassNextStagedInstallerCheck) {
- mBypassNextStagedInstallerCheck = false;
- } else if (params.isStaged
- && !isCalledBySystemOrShell(callingUid)
- && !isWhitelistedStagedInstaller(requestedInstallerPackageName)) {
- throw new SecurityException("Installer not allowed to commit staged install");
+ if (params.isStaged && !isCalledBySystemOrShell(callingUid)) {
+ if (mBypassNextStagedInstallerCheck) {
+ mBypassNextStagedInstallerCheck = false;
+ } else if (!isStagedInstallerAllowed(requestedInstallerPackageName)) {
+ throw new SecurityException("Installer not allowed to commit staged install");
+ }
}
if (!params.isMultiPackage) {
@@ -727,7 +752,7 @@
|| callingUid == Process.SHELL_UID;
}
- private boolean isWhitelistedStagedInstaller(String installerName) {
+ private boolean isStagedInstallerAllowed(String installerName) {
return SystemConfig.getInstance().getWhitelistedStagedInstallers().contains(installerName);
}
@@ -867,7 +892,16 @@
@Override
public ParceledListSlice<SessionInfo> getStagedSessions() {
- return mStagingManager.getSessions(Binder.getCallingUid());
+ final List<SessionInfo> result = new ArrayList<>();
+ synchronized (mSessions) {
+ for (int i = 0; i < mSessions.size(); i++) {
+ final PackageInstallerSession session = mSessions.valueAt(i);
+ if (session.isStaged() && !session.isDestroyed()) {
+ result.add(session.generateInfoForCaller(false, Binder.getCallingUid()));
+ }
+ }
+ }
+ return new ParceledListSlice<>(result);
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index a2ada46..ca16d57 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;
@@ -402,7 +405,7 @@
private boolean mDataLoaderFinished = false;
- // TODO(b/159663586): should be protected by mLock
+ @GuardedBy("mLock")
private IncrementalFileStorages mIncrementalFileStorages;
private static final FileFilter sAddedApkFilter = new FileFilter() {
@@ -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.");
}
}
@@ -1127,13 +1125,18 @@
return;
}
if (isMultiPackage()) {
- final SparseIntArray remainingSessions = mChildSessionIds.clone();
+ final SparseIntArray remainingSessions;
+ final int[] childSessionIds;
+ synchronized (mLock) {
+ remainingSessions = mChildSessionIds.clone();
+ childSessionIds = mChildSessionIds.copyKeys();
+ }
final IntentSender childIntentSender =
new ChildStatusIntentReceiver(remainingSessions, statusReceiver)
.getIntentSender();
boolean sealFailed = false;
- for (int i = mChildSessionIds.size() - 1; i >= 0; --i) {
- final int childSessionId = mChildSessionIds.keyAt(i);
+ for (int i = childSessionIds.length - 1; i >= 0; --i) {
+ final int childSessionId = childSessionIds[i];
// seal all children, regardless if any of them fail; we'll throw/return
// as appropriate once all children have been processed
if (!mSessionProvider.getSession(childSessionId)
@@ -1165,13 +1168,17 @@
}
if (isMultiPackage()) {
- int childCount = mChildSessionIds.size();
+ final int[] childSessionIds;
+ synchronized (mLock) {
+ childSessionIds = mChildSessionIds.copyKeys();
+ }
+ int childCount = childSessionIds.length;
// This will contain all child sessions that do not encounter an unrecoverable failure
ArrayList<PackageInstallerSession> nonFailingSessions = new ArrayList<>(childCount);
for (int i = childCount - 1; i >= 0; --i) {
- final int childSessionId = mChildSessionIds.keyAt(i);
+ final int childSessionId = childSessionIds[i];
// commit all children, regardless if any of them fail; we'll throw/return
// as appropriate once all children have been processed
try {
@@ -1668,11 +1675,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 +1756,7 @@
failure.error, failure.getLocalizedMessage(), null);
return;
}
- mPm.installStage(installingChildSessions);
+ mPm.installStage(installingSession, installingChildSessions);
} else {
mPm.installStage(installingSession);
}
@@ -1873,6 +1875,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 +1912,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 +2185,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)) {
@@ -2551,7 +2597,8 @@
* Adds a child session ID without any safety / sanity checks. This should only be used to
* build a session from XML or similar.
*/
- void addChildSessionIdInternal(int sessionId) {
+ @GuardedBy("mLock")
+ void addChildSessionIdLocked(int sessionId) {
mChildSessionIds.put(sessionId, 0);
}
@@ -2927,7 +2974,10 @@
@Override
public int[] getChildSessionIds() {
- final int[] childSessionIds = mChildSessionIds.copyKeys();
+ final int[] childSessionIds;
+ synchronized (mLock) {
+ childSessionIds = mChildSessionIds.copyKeys();
+ }
if (childSessionIds != null) {
return childSessionIds;
}
@@ -2953,7 +3003,7 @@
return;
}
childSession.setParentSessionId(this.sessionId);
- addChildSessionIdInternal(childSessionId);
+ addChildSessionIdLocked(childSessionId);
}
}
@@ -3120,10 +3170,10 @@
for (FileBridge bridge : mBridges) {
bridge.forceClose();
}
- }
- if (mIncrementalFileStorages != null) {
- mIncrementalFileStorages.cleanUp();
- mIncrementalFileStorages = null;
+ if (mIncrementalFileStorages != null) {
+ mIncrementalFileStorages.cleanUp();
+ mIncrementalFileStorages = null;
+ }
}
// For staged sessions, we don't delete the directory where the packages have been copied,
// since these packages are supposed to be read on reboot.
@@ -3160,9 +3210,11 @@
}
private void cleanStageDir() {
- if (mIncrementalFileStorages != null) {
- mIncrementalFileStorages.cleanUp();
- mIncrementalFileStorages = null;
+ synchronized (mLock) {
+ if (mIncrementalFileStorages != null) {
+ mIncrementalFileStorages.cleanUp();
+ mIncrementalFileStorages = null;
+ }
}
try {
mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
@@ -3183,6 +3235,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..9f3db70 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.
@@ -11477,15 +11492,14 @@
}
final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride, pkgSetting);
+ final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
if (needToDeriveAbi) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
- final boolean extractNativeLibs = !AndroidPackageUtils.isLibrary(parsedPackage);
final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi =
- packageAbiHelper.derivePackageAbi(parsedPackage,
- pkgSetting.getPkgState().isUpdatedSystemApp(), cpuAbiOverride,
- extractNativeLibs);
+ packageAbiHelper.derivePackageAbi(parsedPackage, isUpdatedSystemApp,
+ cpuAbiOverride);
derivedAbi.first.applyTo(parsedPackage);
derivedAbi.second.applyTo(parsedPackage);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -11495,15 +11509,15 @@
// structure. Try to detect abi based on directory structure.
String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
- if (parsedPackage.isSystem() && !pkgSetting.getPkgState().isUpdatedSystemApp() &&
- pkgRawPrimaryCpuAbi == null) {
+ if (parsedPackage.isSystem() && !isUpdatedSystemApp
+ && pkgRawPrimaryCpuAbi == null) {
final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis(
parsedPackage);
abis.applyTo(parsedPackage);
abis.applyTo(pkgSetting);
final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.getNativeLibraryPaths(parsedPackage, pkgSetting,
- sAppLib32InstallDir);
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
+ isUpdatedSystemApp, sAppLib32InstallDir);
nativeLibraryPaths.applyTo(parsedPackage);
}
} else {
@@ -11514,8 +11528,8 @@
.setSecondaryCpuAbi(secondaryCpuAbiFromSettings);
final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.getNativeLibraryPaths(parsedPackage,
- pkgSetting, sAppLib32InstallDir);
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
+ isUpdatedSystemApp, sAppLib32InstallDir);
nativeLibraryPaths.applyTo(parsedPackage);
if (DEBUG_ABI_SELECTION) {
@@ -11540,7 +11554,7 @@
// ABIs we determined during compilation, but the path will depend on the final
// package path (after the rename away from the stage path).
final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.getNativeLibraryPaths(parsedPackage, pkgSetting,
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isUpdatedSystemApp,
sAppLib32InstallDir);
nativeLibraryPaths.applyTo(parsedPackage);
}
@@ -12806,11 +12820,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 +14316,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 +14696,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 +14720,8 @@
this.mChildParams.add(childParams);
}
this.mCurrentState = new ArrayMap<>(mChildParams.size());
+ this.mVerificationState = new ArrayMap<>(mChildParams.size());
+ mVerificationObserver = parent.getVerificationObserver();
}
@Override
@@ -14729,6 +14738,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 +14762,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 +14791,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 +14814,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 +14824,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 +14863,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 +15429,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 +15510,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 +15524,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 +15542,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 +15644,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 +16110,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 +17179,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 +17204,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 +17250,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 +17456,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");
}
}
@@ -17434,7 +17484,6 @@
scanFlags |= SCAN_NO_DEX;
try {
- final boolean extractNativeLibs = !AndroidPackageUtils.isLibrary(parsedPackage);
PackageSetting pkgSetting;
synchronized (mLock) {
pkgSetting = mSettings.getPackageLPr(pkgName);
@@ -17449,7 +17498,7 @@
final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
derivedAbi = mInjector.getAbiHelper().derivePackageAbi(parsedPackage,
isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred,
- abiOverride, extractNativeLibs);
+ abiOverride);
derivedAbi.first.applyTo(parsedPackage);
derivedAbi.second.applyTo(parsedPackage);
} catch (PackageManagerException pme) {
@@ -17598,7 +17647,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 +17655,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 +19172,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 +19230,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 +25569,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 +25580,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 +25605,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..14da9aa 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -38,7 +38,6 @@
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
-import android.content.pm.ParceledListSlice;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
@@ -164,6 +163,7 @@
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_BOOT_COMPLETED && sStagingManager != null) {
sStagingManager.markStagedSessionsAsSuccessful();
+ sStagingManager.markBootCompleted();
}
}
}
@@ -179,18 +179,8 @@
}
}
- ParceledListSlice<PackageInstaller.SessionInfo> getSessions(int callingUid) {
- final List<PackageInstaller.SessionInfo> result = new ArrayList<>();
- synchronized (mStagedSessions) {
- for (int i = 0; i < mStagedSessions.size(); i++) {
- final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
- if (stagedSession.isDestroyed()) {
- continue;
- }
- result.add(stagedSession.generateInfoForCaller(false /*icon*/, callingUid));
- }
- }
- return new ParceledListSlice<>(result);
+ private void markBootCompleted() {
+ mApexManager.markBootCompleted();
}
/**
diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
index 88938b2..c8e3648 100644
--- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
+++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
@@ -206,6 +206,16 @@
}
/**
+ * Deletes snapshots of the credential encrypted apex data directories for the specified user,
+ * for the given rollback id. This method will be a no-op if the user is not unlocked.
+ */
+ public void destroyApexCeSnapshots(int userId, int rollbackId) {
+ if (!isUserCredentialLocked(userId)) {
+ mApexManager.destroyCeSnapshots(userId, rollbackId);
+ }
+ }
+
+ /**
* Commits the pending backups and restores for a given {@code userId} and {@code rollback}. If
* the rollback has a pending backup, it is updated with a mapping from {@code userId} to inode
* of the CE user data snapshot.
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index c347780..2db6043 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -42,6 +42,7 @@
import android.os.UserManager;
import android.os.ext.SdkExtensions;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseIntArray;
@@ -61,9 +62,9 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.function.Consumer;
-
/**
* Information about a rollback available for a set of atomically installed packages.
*
@@ -699,11 +700,13 @@
void delete(AppDataRollbackHelper dataHelper) {
assertInWorkerThread();
boolean containsApex = false;
+ Set<Integer> apexUsers = new ArraySet<>();
for (PackageRollbackInfo pkgInfo : info.getPackages()) {
+ List<Integer> snapshottedUsers = pkgInfo.getSnapshottedUsers();
if (pkgInfo.isApex()) {
containsApex = true;
+ apexUsers.addAll(snapshottedUsers);
} else {
- List<Integer> snapshottedUsers = pkgInfo.getSnapshottedUsers();
for (int i = 0; i < snapshottedUsers.size(); i++) {
// Destroy app data snapshot.
int userId = snapshottedUsers.get(i);
@@ -714,6 +717,9 @@
}
if (containsApex) {
dataHelper.destroyApexDeSnapshots(info.getRollbackId());
+ for (int user : apexUsers) {
+ dataHelper.destroyApexCeSnapshots(user, info.getRollbackId());
+ }
}
RollbackStore.deleteRollback(this);
diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
index 734b718..de06c92 100644
--- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
+++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
@@ -49,11 +49,8 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.EventLogTags;
import com.android.server.SystemService;
-import com.android.server.pm.InstructionSets;
import com.android.server.pm.PackageManagerService;
-import dalvik.system.VMRuntime;
-
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -214,7 +211,7 @@
newLevel = State.LEVEL_FULL;
} else if (usableBytes <= lowBytes) {
newLevel = State.LEVEL_LOW;
- } else if (StorageManager.UUID_DEFAULT.equals(uuid) && !isBootImageOnDisk()
+ } else if (StorageManager.UUID_DEFAULT.equals(uuid)
&& usableBytes < BOOT_IMAGE_STORAGE_REQUIREMENT) {
newLevel = State.LEVEL_LOW;
} else {
@@ -261,15 +258,6 @@
};
}
- private static boolean isBootImageOnDisk() {
- for (String instructionSet : InstructionSets.getAllDexCodeInstructionSets()) {
- if (!VMRuntime.isBootClassPathOnDisk(instructionSet)) {
- return false;
- }
- }
- return true;
- }
-
@Override
public void onStart() {
final Context context = getContext();
@@ -481,15 +469,8 @@
final CharSequence title = context.getText(
com.android.internal.R.string.low_internal_storage_view_title);
- final CharSequence details;
- if (StorageManager.UUID_DEFAULT.equals(uuid)) {
- details = context.getText(isBootImageOnDisk()
- ? com.android.internal.R.string.low_internal_storage_view_text
- : com.android.internal.R.string.low_internal_storage_view_text_no_boot);
- } else {
- details = context.getText(
- com.android.internal.R.string.low_internal_storage_view_text);
- }
+ final CharSequence details = context.getText(
+ com.android.internal.R.string.low_internal_storage_view_text);
PendingIntent intent = PendingIntent.getActivityAsUser(context, 0, lowMemIntent, 0,
null, UserHandle.CURRENT);
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index 37df548..6dc1d692 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -53,16 +53,14 @@
private final Context mContext;
@GuardedBy("mLock")
private final SparseArray<StorageUserConnection> mConnections = new SparseArray<>();
- private final boolean mIsFuseEnabled;
private volatile ComponentName mExternalStorageServiceComponent;
private volatile String mExternalStorageServicePackageName;
private volatile int mExternalStorageServiceAppId;
private volatile boolean mIsResetting;
- public StorageSessionController(Context context, boolean isFuseEnabled) {
+ public StorageSessionController(Context context) {
mContext = Objects.requireNonNull(context);
- mIsFuseEnabled = isFuseEnabled;
}
/**
@@ -361,6 +359,6 @@
}
private boolean shouldHandle(@Nullable VolumeInfo vol) {
- return mIsFuseEnabled && !mIsResetting && (vol == null || isEmulatedOrPublic(vol));
+ return !mIsResetting && (vol == null || isEmulatedOrPublic(vol));
}
}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 32f8da8..3406bd9 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -28,6 +28,7 @@
import android.os.Binder;
import android.os.Handler;
import android.provider.Settings;
+import android.util.IndentingPrintWriter;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
@@ -139,7 +140,9 @@
@Nullable String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- mTimeDetectorStrategy.dump(pw, args);
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+ mTimeDetectorStrategy.dump(ipw, args);
+ ipw.flush();
}
private void enforceSuggestTelephonyTimePermission() {
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index 9a39d24..e943978 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -17,25 +17,25 @@
package com.android.server.timedetector;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
import android.os.TimestampedValue;
+import android.util.IndentingPrintWriter;
-import java.io.PrintWriter;
+import com.android.server.timezonedetector.Dumpable;
/**
* The interface for the class that implements the time detection algorithm used by the
* {@link TimeDetectorService}.
*
* <p>Most calls will be handled by a single thread but that is not true for all calls. For example
- * {@link #dump(PrintWriter, String[])}) may be called on a different thread so implementations must
- * handle thread safety.
+ * {@link #dump(IndentingPrintWriter, String[])}) may be called on a different thread so
+ * implementations must handle thread safety.
*
* @hide
*/
-public interface TimeDetectorStrategy {
+public interface TimeDetectorStrategy extends Dumpable {
/**
* The interface used by the strategy to interact with the surrounding service.
@@ -94,9 +94,6 @@
/** Handle the auto-time setting being toggled on or off. */
void handleAutoTimeDetectionChanged();
- /** Dump debug information. */
- void dump(@NonNull PrintWriter pw, @Nullable String[] args);
-
// Utility methods below are to be moved to a better home when one becomes more obvious.
/**
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index ceb5485..fe0e82e 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -24,16 +24,15 @@
import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
import android.os.TimestampedValue;
+import android.util.IndentingPrintWriter;
import android.util.LocalLog;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.server.timezonedetector.ArrayMapWithHistory;
import com.android.server.timezonedetector.ReferenceWithHistory;
-import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -203,8 +202,7 @@
}
@Override
- public synchronized void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
- IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ public synchronized void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
ipw.println("TimeDetectorStrategy:");
ipw.increaseIndent(); // level 1
@@ -232,7 +230,6 @@
ipw.decreaseIndent(); // level 2
ipw.decreaseIndent(); // level 1
- ipw.flush();
}
@GuardedBy("this")
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/timezonedetector/ArrayMapWithHistory.java b/services/core/java/com/android/server/timezonedetector/ArrayMapWithHistory.java
index 3274f0e..d6fdddf 100644
--- a/services/core/java/com/android/server/timezonedetector/ArrayMapWithHistory.java
+++ b/services/core/java/com/android/server/timezonedetector/ArrayMapWithHistory.java
@@ -20,10 +20,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
/**
* A partial decorator for {@link ArrayMap} that records historic values for each mapping for
diff --git a/services/core/java/com/android/server/timezonedetector/Dumpable.java b/services/core/java/com/android/server/timezonedetector/Dumpable.java
index 58a5a0b..5603c38 100644
--- a/services/core/java/com/android/server/timezonedetector/Dumpable.java
+++ b/services/core/java/com/android/server/timezonedetector/Dumpable.java
@@ -15,24 +15,27 @@
*/
package com.android.server.timezonedetector;
-import java.io.PrintWriter;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.IndentingPrintWriter;
/** An interface for components that can write their internal state to dumpsys logs. */
public interface Dumpable {
/** Dump internal state. */
- void dump(PrintWriter pw, String[] args);
+ void dump(@NonNull IndentingPrintWriter pw, @Nullable String[] args);
/**
* An interface that can be used expose when one component allows another to be registered so
* that it is dumped at the same time.
*/
- interface Dumpee {
+ interface Container {
/**
* Registers the supplied {@link Dumpable}. When the implementation is dumped
- * {@link Dumpable#dump(PrintWriter, String[])} should be called on the {@code dumpable}.
+ * {@link Dumpable#dump(IndentingPrintWriter, String[])} should be called on the
+ * {@code dumpable}.
*/
- void addDumpable(Dumpable dumpable);
+ void addDumpable(@NonNull Dumpable dumpable);
}
}
diff --git a/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java b/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
index 165419a..b63df05 100644
--- a/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
+++ b/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
@@ -19,8 +19,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-
-import com.android.internal.util.IndentingPrintWriter;
+import android.util.IndentingPrintWriter;
import java.util.ArrayDeque;
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
index d833586..3d9ec64 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
@@ -24,7 +24,7 @@
*
* @hide
*/
-public interface TimeZoneDetectorInternal extends Dumpable.Dumpee {
+public interface TimeZoneDetectorInternal extends Dumpable.Container {
/**
* Suggests the current time zone, determined using geolocation, to the detector. The
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
index d8db1ba..4464f7d 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
@@ -49,7 +49,7 @@
}
@Override
- public void addDumpable(Dumpable dumpable) {
+ public void addDumpable(@NonNull Dumpable dumpable) {
mTimeZoneDetectorStrategy.addDumpable(dumpable);
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index bb4f56d..d9415ce 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -36,6 +36,7 @@
import android.os.ShellCallback;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -118,7 +119,7 @@
Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
new ContentObserver(handler) {
public void onChange(boolean selfChange) {
- service.handleAutoTimeZoneDetectionChanged();
+ service.handleAutoTimeZoneConfigChanged();
}
});
return service;
@@ -272,12 +273,14 @@
@Nullable String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- mTimeZoneDetectorStrategy.dump(pw, args);
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+ mTimeZoneDetectorStrategy.dump(ipw, args);
+ ipw.flush();
}
/** Internal method for handling the auto time zone configuration being changed. */
@VisibleForTesting
- public void handleAutoTimeZoneDetectionChanged() {
+ public void handleAutoTimeZoneConfigChanged() {
mHandler.post(mTimeZoneDetectorStrategy::handleAutoTimeZoneConfigChanged);
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index 497ff3e..f947a655 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -21,8 +21,7 @@
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
import android.app.timezonedetector.TimeZoneCapabilities;
import android.app.timezonedetector.TimeZoneConfiguration;
-
-import java.io.PrintWriter;
+import android.util.IndentingPrintWriter;
/**
* The interface for the class that implements the time detection algorithm used by the
@@ -32,12 +31,12 @@
* and what to set it to.
*
* <p>Most calls will be handled by a single thread but that is not true for all calls. For example
- * {@link #dump(PrintWriter, String[])}) may be called on a different thread so implementations must
- * handle thread safety.
+ * {@link #dump(IndentingPrintWriter, String[])}) may be called on a different thread so
+ * implementations mustvhandle thread safety.
*
* @hide
*/
-public interface TimeZoneDetectorStrategy extends Dumpable.Dumpee {
+public interface TimeZoneDetectorStrategy extends Dumpable, Dumpable.Container {
/** A listener for strategy events. */
interface StrategyListener {
@@ -91,9 +90,4 @@
* Called when there has been a change to the automatic time zone detection configuration.
*/
void handleAutoTimeZoneConfigChanged();
-
- /**
- * Dumps internal state such as field values.
- */
- void dump(PrintWriter pw, String[] args);
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index ab9a773..9c36c39 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -32,14 +32,13 @@
import android.app.timezonedetector.TimeZoneCapabilities;
import android.app.timezonedetector.TimeZoneConfiguration;
import android.content.Context;
+import android.util.IndentingPrintWriter;
import android.util.LocalLog;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -517,7 +516,7 @@
}
@Override
- public synchronized void addDumpable(Dumpable dumpable) {
+ public synchronized void addDumpable(@NonNull Dumpable dumpable) {
mDumpables.add(dumpable);
}
@@ -525,8 +524,7 @@
* Dumps internal state such as field values.
*/
@Override
- public synchronized void dump(PrintWriter pw, String[] args) {
- IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ public synchronized void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
ipw.println("TimeZoneDetectorStrategy:");
ipw.increaseIndent(); // level 1
@@ -549,7 +547,6 @@
for (Dumpable dumpable : mDumpables) {
dumpable.dump(ipw, args);
}
- ipw.flush();
}
/**
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 5f63233..f5eed30 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -114,7 +114,7 @@
private static final boolean DEBUG = false;
private static final String TAG = "UriGrantsManagerService";
// Maximum number of persisted Uri grants a package is allowed
- private static final int MAX_PERSISTED_URI_GRANTS = 128;
+ private static final int MAX_PERSISTED_URI_GRANTS = 512;
private static final boolean ENABLE_DYNAMIC_PERMISSIONS = true;
private final Object mLock = new Object();
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index f5d6803..ecba3f9 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1357,7 +1357,12 @@
addedWindows.clear();
// Gets the top focused display Id and window token for supporting multi-display.
- topFocusedDisplayId = mService.mRoot.getTopFocusedDisplayContent().getDisplayId();
+ // If this top focused display is an embedded one, using its parent display as the
+ // top focused display.
+ final DisplayContent topFocusedDisplayContent =
+ mService.mRoot.getTopFocusedDisplayContent();
+ topFocusedDisplayId = isEmbeddedDisplay(topFocusedDisplayContent) ? mDisplayId
+ : topFocusedDisplayContent.getDisplayId();
topFocusedWindowToken = topFocusedWindowState.mClient.asBinder();
}
mCallback.onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index a5e945b..a87fe61 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++;
}
@@ -4591,8 +4605,8 @@
return false;
}
- final boolean behindFullscreenActivity = stack.checkBehindFullscreenActivity(
- this, null /* handleBehindFullscreenActivity */);
+ final boolean behindFullscreenActivity = !stack.shouldBeVisible(null /* starting */)
+ || stack.getOccludingActivityAbove(this) != null;
return shouldBeVisible(behindFullscreenActivity, false /* ignoringKeyguard */);
}
@@ -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,
@@ -7503,22 +7516,16 @@
}
/**
- * @return {@code true} if this is the resumed activity on its current display, {@code false}
+ * @return {@code true} if this is the focused activity on its current display, {@code false}
* otherwise.
*/
- boolean isResumedActivityOnDisplay() {
+ boolean isFocusedActivityOnDisplay() {
final DisplayContent display = getDisplay();
if (display == null) {
return false;
}
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- final ActivityRecord resumedActivity = taskDisplayArea.getFocusedActivity();
- if (resumedActivity != null) {
- return resumedActivity == this;
- }
- }
- return false;
+ return display.forAllTaskDisplayAreas(taskDisplayArea ->
+ taskDisplayArea.getFocusedActivity() == this);
}
@@ -7605,7 +7612,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..ea9aab7 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -328,81 +328,6 @@
}
}
- private final CheckBehindFullscreenActivityHelper mCheckBehindFullscreenActivityHelper =
- new CheckBehindFullscreenActivityHelper();
- private class CheckBehindFullscreenActivityHelper {
- private boolean mAboveTop;
- private boolean mBehindFullscreenActivity;
- private ActivityRecord mToCheck;
- private Consumer<ActivityRecord> mHandleBehindFullscreenActivity;
- private boolean mHandlingOccluded;
-
- private void reset(ActivityRecord toCheck,
- Consumer<ActivityRecord> handleBehindFullscreenActivity) {
- mToCheck = toCheck;
- mHandleBehindFullscreenActivity = handleBehindFullscreenActivity;
- mAboveTop = true;
- mBehindFullscreenActivity = false;
-
- if (!shouldBeVisible(null)) {
- // The stack is not visible, so no activity in it should be displaying a starting
- // window. Mark all activities below top and behind fullscreen.
- mAboveTop = false;
- mBehindFullscreenActivity = true;
- }
-
- mHandlingOccluded = mToCheck == null && mHandleBehindFullscreenActivity != null;
- }
-
- boolean process(ActivityRecord toCheck,
- Consumer<ActivityRecord> handleBehindFullscreenActivity) {
- reset(toCheck, handleBehindFullscreenActivity);
-
- if (!mHandlingOccluded && mBehindFullscreenActivity) {
- return true;
- }
-
- final ActivityRecord topActivity = topRunningActivity();
- final PooledFunction f = PooledLambda.obtainFunction(
- CheckBehindFullscreenActivityHelper::processActivity, this,
- PooledLambda.__(ActivityRecord.class), topActivity);
- forAllActivities(f);
- f.recycle();
-
- return mBehindFullscreenActivity;
- }
-
- /** Returns {@code true} to stop the outer loop and indicate the result is computed. */
- private boolean processActivity(ActivityRecord r, ActivityRecord topActivity) {
- if (mAboveTop) {
- if (r == topActivity) {
- if (r == mToCheck) {
- // It is the top activity in a visible stack.
- mBehindFullscreenActivity = false;
- return true;
- }
- mAboveTop = false;
- }
- mBehindFullscreenActivity |= r.occludesParent();
- return false;
- }
-
- if (mHandlingOccluded) {
- // Iterating through all occluded activities.
- if (mBehindFullscreenActivity) {
- mHandleBehindFullscreenActivity.accept(r);
- }
- } else if (r == mToCheck) {
- return true;
- } else if (mBehindFullscreenActivity) {
- // It is occluded before {@param toCheck} is found.
- return true;
- }
- mBehindFullscreenActivity |= r.occludesParent();
- return false;
- }
- }
-
// TODO: Can we just loop through WindowProcessController#mActivities instead of doing this?
private final RemoveHistoryRecordsForApp mRemoveHistoryRecordsForApp =
new RemoveHistoryRecordsForApp();
@@ -925,8 +850,6 @@
void awakeFromSleepingLocked() {
// Ensure activities are no longer sleeping.
forAllActivities((Consumer<ActivityRecord>) (r) -> r.setSleeping(false));
- ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- false /* preserveWindows */);
if (mPausingActivity != null) {
Slog.d(TAG, "awakeFromSleepingLocked: previously pausing activity didn't pause");
mPausingActivity.activityPaused(true);
@@ -1436,25 +1359,6 @@
}
}
- /** @see ActivityRecord#cancelInitializing() */
- void cancelInitializingActivities() {
- // We don't want to clear starting window for activities that aren't behind fullscreen
- // activities as we need to display their starting window until they are done initializing.
- checkBehindFullscreenActivity(null /* toCheck */, ActivityRecord::cancelInitializing);
- }
-
- /**
- * If an activity {@param toCheck} is given, this method returns {@code true} if the activity
- * is occluded by any fullscreen activity. If there is no {@param toCheck} and the handling
- * function {@param handleBehindFullscreenActivity} is given, this method will pass all occluded
- * activities to the function.
- */
- boolean checkBehindFullscreenActivity(ActivityRecord toCheck,
- Consumer<ActivityRecord> handleBehindFullscreenActivity) {
- return mCheckBehindFullscreenActivityHelper.process(
- toCheck, handleBehindFullscreenActivity);
- }
-
/**
* Ensure that the top activity in the stack is resumed.
*
@@ -2029,7 +1933,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
@@ -2411,7 +2320,7 @@
forAllActivities(ActivityRecord::removeLaunchTickRunnable);
}
- private void updateTransitLocked(int transit, ActivityOptions options) {
+ private void updateTransitLocked(int transit, ActivityOptions options, boolean forceOverride) {
if (options != null) {
ActivityRecord r = topRunningActivity();
if (r != null && !r.isState(RESUMED)) {
@@ -2420,7 +2329,8 @@
ActivityOptions.abort(options);
}
}
- getDisplay().mDisplayContent.prepareAppTransition(transit, false);
+ getDisplay().mDisplayContent.prepareAppTransition(transit, false,
+ 0 /* flags */, forceOverride);
}
final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
@@ -2440,8 +2350,17 @@
// nothing to do!
if (noAnimation) {
ActivityOptions.abort(options);
+ } else if (isSingleTaskInstance()) {
+ // When a task is moved front on the display which can only contain one task, start
+ // a special transition.
+ // {@link AppTransitionController#handleAppTransitionReady} later picks up the
+ // transition, and schedules
+ // {@link ITaskStackListener#onSingleTaskDisplayDrawn} callback which is triggered
+ // after contents are drawn on the display.
+ updateTransitLocked(TRANSIT_SHOW_SINGLE_TASK_DISPLAY, options,
+ true /* forceOverride */);
} else {
- updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
+ updateTransitLocked(TRANSIT_TASK_TO_FRONT, options, false /* forceOverride */);
}
return;
}
@@ -2487,9 +2406,13 @@
mStackSupervisor.mNoAnimActivities.add(r);
}
ActivityOptions.abort(options);
+ } else if (isSingleTaskInstance()) {
+ updateTransitLocked(TRANSIT_SHOW_SINGLE_TASK_DISPLAY, options,
+ true /* forceOverride */);
} else {
- updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
+ updateTransitLocked(TRANSIT_TASK_TO_FRONT, options, false /* forceOverride */);
}
+
// If a new task is moved to the front, then mark the existing top activity as
// supporting
@@ -2643,16 +2566,6 @@
task.setBounds(task.isResizeable() ? bounds : null);
}
- /**
- * Returns the top-most activity that occludes the given one, or @{code null} if none.
- */
- @Nullable
- private ActivityRecord getOccludingActivityAbove(ActivityRecord activity) {
- ActivityRecord top = getActivity((ar) -> ar.occludesParent(),
- true /* traverseTopToBottom */, activity);
- return top != activity ? top : null;
- }
-
boolean willActivityBeVisible(IBinder token) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r == null) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index bf85db2..4e1d789 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -218,7 +218,6 @@
import android.service.voice.VoiceInteractionManagerInternal;
import android.sysprop.DisplayProperties;
import android.telecom.TelecomManager;
-import android.text.TextUtils;
import android.text.format.TimeMigrationUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -1956,7 +1955,7 @@
r.immersive = immersive;
// update associated state if we're frontmost
- if (r.isResumedActivityOnDisplay()) {
+ if (r.isFocusedActivityOnDisplay()) {
if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE, "Frontmost changed immersion: "+ r);
applyUpdateLockStateLocked(r);
}
@@ -4007,8 +4006,7 @@
synchronized (mGlobalLock) {
ActivityRecord record = ActivityRecord.isInStackLocked(token);
if (record == null) {
- throw new IllegalArgumentException("reportSizeConfigurations: ActivityRecord not "
- + "found for: " + token);
+ return;
}
record.setSizeConfigurations(horizontalSizeConfiguration,
verticalSizeConfigurations, smallestSizeConfigurations);
@@ -4322,7 +4320,7 @@
r.requestedVrComponent = (enabled) ? packageName : null;
// Update associated state if this activity is currently focused
- if (r.isResumedActivityOnDisplay()) {
+ if (r.isFocusedActivityOnDisplay()) {
applyUpdateVrModeLocked(r);
}
return 0;
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 6f3ddc9..201473e 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;
@@ -31,6 +30,7 @@
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.WindowContainerChildProto.DISPLAY_AREA;
+import android.annotation.Nullable;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.util.proto.ProtoOutputStream;
@@ -41,7 +41,9 @@
import com.android.server.protolog.common.ProtoLog;
import java.util.Comparator;
+import java.util.function.BiFunction;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.function.Predicate;
/**
@@ -140,11 +142,108 @@
return DISPLAY_AREA;
}
+ @Override
+ final DisplayArea asDisplayArea() {
+ return this;
+ }
+
+ @Override
void forAllDisplayAreas(Consumer<DisplayArea> callback) {
super.forAllDisplayAreas(callback);
callback.accept(this);
}
+ @Override
+ boolean forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean> callback,
+ boolean traverseTopToBottom) {
+ // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children.
+ if (mType != DisplayArea.Type.ANY) {
+ return false;
+ }
+
+ int childCount = mChildren.size();
+ int i = traverseTopToBottom ? childCount - 1 : 0;
+ while (i >= 0 && i < childCount) {
+ T child = mChildren.get(i);
+ // Only traverse if the child is a DisplayArea.
+ if (child.asDisplayArea() != null && child.asDisplayArea()
+ .forAllTaskDisplayAreas(callback, traverseTopToBottom)) {
+ return true;
+ }
+ i += traverseTopToBottom ? -1 : 1;
+ }
+ return false;
+ }
+
+ @Override
+ void forAllTaskDisplayAreas(Consumer<TaskDisplayArea> callback, boolean traverseTopToBottom) {
+ // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children.
+ if (mType != DisplayArea.Type.ANY) {
+ return;
+ }
+
+ int childCount = mChildren.size();
+ int i = traverseTopToBottom ? childCount - 1 : 0;
+ while (i >= 0 && i < childCount) {
+ T child = mChildren.get(i);
+ // Only traverse if the child is a DisplayArea.
+ if (child.asDisplayArea() != null) {
+ child.asDisplayArea().forAllTaskDisplayAreas(callback, traverseTopToBottom);
+ }
+ i += traverseTopToBottom ? -1 : 1;
+ }
+ }
+
+ @Nullable
+ @Override
+ <R> R reduceOnAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R> accumulator,
+ @Nullable R initValue, boolean traverseTopToBottom) {
+ // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children.
+ if (mType != DisplayArea.Type.ANY) {
+ return initValue;
+ }
+
+ int childCount = mChildren.size();
+ int i = traverseTopToBottom ? childCount - 1 : 0;
+ R result = initValue;
+ while (i >= 0 && i < childCount) {
+ T child = mChildren.get(i);
+ // Only traverse if the child is a DisplayArea.
+ if (child.asDisplayArea() != null) {
+ result = (R) child.asDisplayArea()
+ .reduceOnAllTaskDisplayAreas(accumulator, result, traverseTopToBottom);
+ }
+ i += traverseTopToBottom ? -1 : 1;
+ }
+ return result;
+ }
+
+ @Nullable
+ @Override
+ <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback,
+ boolean traverseTopToBottom) {
+ // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children.
+ if (mType != DisplayArea.Type.ANY) {
+ return null;
+ }
+
+ int childCount = mChildren.size();
+ int i = traverseTopToBottom ? childCount - 1 : 0;
+ while (i >= 0 && i < childCount) {
+ T child = mChildren.get(i);
+ // Only traverse if the child is a DisplayArea.
+ if (child.asDisplayArea() != null) {
+ R result = (R) child.asDisplayArea()
+ .getItemFromTaskDisplayAreas(callback, traverseTopToBottom);
+ if (result != null) {
+ return result;
+ }
+ }
+ i += traverseTopToBottom ? -1 : 1;
+ }
+ return null;
+ }
+
void setOrganizer(IDisplayAreaOrganizer organizer) {
if (mOrganizer == organizer) return;
IDisplayAreaOrganizer lastOrganizer = mOrganizer;
@@ -253,6 +352,7 @@
req = mLastKeyguardForcedOrientation;
}
}
+ mLastOrientationSource = win;
return req;
}
}
@@ -263,15 +363,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..a161aca 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).
@@ -95,31 +63,21 @@
*/
public abstract List<DisplayArea<? extends WindowContainer>> getDisplayAreas(int featureId);
- /**
- * @return the number of task display areas on the display.
- */
- public int getTaskDisplayAreaCount() {
- return mTaskDisplayAreas.size();
- }
-
- /**
- * @return the task display area at index.
- */
- public TaskDisplayArea getTaskDisplayAreaAt(int index) {
- return mTaskDisplayAreas.get(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 +91,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 +109,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..465d089 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
@@ -23,16 +23,24 @@
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
+import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
+
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.policy.WindowManagerPolicy;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.function.BiFunction;
/**
* A builder for instantiating a complex {@link DisplayAreaPolicy}
@@ -51,7 +59,7 @@
* .build(...)
*
* // Builds a policy with the following hierarchy:
- * - DisplayArea.Root
+ * - RootDisplayArea
* - Magnification
* - DisplayArea.Tokens (Wallpapers are attached here)
* - TaskDisplayArea
@@ -62,11 +70,299 @@
*
* </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 ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders = new ArrayList<>();
- private final ArrayList<Feature> mFeatures = new ArrayList<>();
+ /**
+ * When a window is created, the policy will use this function to select the
+ * {@link RootDisplayArea} to place that window in. The selected root can be either the one of
+ * the {@link #mRootHierarchyBuilder} or the one of any of the
+ * {@link #mDisplayAreaGroupHierarchyBuilders}.
+ **/
+ @Nullable private BiFunction<WindowToken, Bundle, RootDisplayArea> mSelectRootForWindowFunc;
+
+ /** Defines the root hierarchy for the whole logical display. */
+ DisplayAreaPolicyBuilder setRootHierarchy(HierarchyBuilder rootHierarchyBuilder) {
+ mRootHierarchyBuilder = rootHierarchyBuilder;
+ return this;
+ }
+
+ /**
+ * Defines a DisplayAreaGroup hierarchy. Its root will be added as a child of the root
+ * hierarchy.
+ */
+ DisplayAreaPolicyBuilder addDisplayAreaGroupHierarchy(
+ HierarchyBuilder displayAreaGroupHierarchy) {
+ mDisplayAreaGroupHierarchyBuilders.add(displayAreaGroupHierarchy);
+ return this;
+ }
+
+ /** The policy will use this function to find the root to place windows in. */
+ DisplayAreaPolicyBuilder setSelectRootForWindowFunc(
+ BiFunction<WindowToken, Bundle, RootDisplayArea> selectRootForWindowFunc) {
+ mSelectRootForWindowFunc = selectRootForWindowFunc;
+ return this;
+ }
+
+ /** Makes sure the setting meets the requirement. */
+ private void validate() {
+ if (mRootHierarchyBuilder == null) {
+ throw new IllegalStateException("Root must be set for the display area policy.");
+ }
+
+ boolean containsImeContainer = mRootHierarchyBuilder.mImeContainer != null;
+ boolean containsDefaultTda = containsDefaultTaskDisplayArea(mRootHierarchyBuilder);
+ for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) {
+ HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i);
+ if (hierarchyBuilder.mTaskDisplayAreas.isEmpty()) {
+ throw new IllegalStateException(
+ "DisplayAreaGroup must contain at least one TaskDisplayArea.");
+ }
+
+ containsImeContainer = containsImeContainer || hierarchyBuilder.mImeContainer != null;
+ containsDefaultTda = containsDefaultTda
+ || containsDefaultTaskDisplayArea(hierarchyBuilder);
+ }
+
+ if (!containsImeContainer) {
+ throw new IllegalStateException("IME container must be set.");
+ }
+
+ if (!containsDefaultTda) {
+ throw new IllegalStateException("There must be a default TaskDisplayArea.");
+ }
+ }
+
+ /** Checks if the given hierarchy contains the default {@link TaskDisplayArea}. */
+ private static boolean containsDefaultTaskDisplayArea(HierarchyBuilder displayAreaHierarchy) {
+ for (int i = 0; i < displayAreaHierarchy.mTaskDisplayAreas.size(); i++) {
+ if (displayAreaHierarchy.mTaskDisplayAreas.get(i).mFeatureId
+ == FEATURE_DEFAULT_TASK_CONTAINER) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ Result build(WindowManagerService wmService) {
+ validate();
+
+ // Attach DA group roots to screen hierarchy before adding windows to group hierarchies.
+ mRootHierarchyBuilder.build(mDisplayAreaGroupHierarchyBuilders);
+ List<RootDisplayArea> displayAreaGroupRoots = new ArrayList<>(
+ mDisplayAreaGroupHierarchyBuilders.size());
+ for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) {
+ HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i);
+ hierarchyBuilder.build();
+ displayAreaGroupRoots.add(hierarchyBuilder.mRoot);
+ }
+ return new Result(wmService, mRootHierarchyBuilder.mRoot, displayAreaGroupRoots,
+ mSelectRootForWindowFunc);
+ }
+
+ /**
+ * 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() {
+ build(null /* displayAreaGroupHierarchyBuilders */);
+ }
+
+ /**
+ * Builds the {@link DisplayArea} hierarchy below root. And adds the roots of those
+ * {@link HierarchyBuilder} as children.
+ */
+ private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
+ 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.
+ addTaskDisplayAreasToApplicationLayer(areaForLayer[layer]);
+ addDisplayAreaGroupsToApplicationLayer(areaForLayer[layer],
+ displayAreaGroupHierarchyBuilders);
+ 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);
+ }
+
+ /** Adds all {@link TaskDisplayArea} to the application layer. */
+ private void addTaskDisplayAreasToApplicationLayer(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);
+ }
+ }
+
+ /** Adds roots of the DisplayAreaGroups to the application layer. */
+ private void addDisplayAreaGroupsToApplicationLayer(
+ DisplayAreaPolicyBuilder.PendingArea parentPendingArea,
+ @Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
+ if (displayAreaGroupHierarchyBuilders == null) {
+ return;
+ }
+ final int count = displayAreaGroupHierarchyBuilders.size();
+ for (int i = 0; i < count; i++) {
+ DisplayAreaPolicyBuilder.PendingArea
+ leafArea = new DisplayAreaPolicyBuilder.PendingArea(
+ null /* feature */, APPLICATION_LAYER, parentPendingArea);
+ leafArea.mExisting = displayAreaGroupHierarchyBuilders.get(i).mRoot;
+ 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 +412,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 +504,20 @@
}
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;
+ final List<RootDisplayArea> mDisplayAreaGroupRoots;
+ final BiFunction<WindowToken, Bundle, RootDisplayArea> mSelectRootForWindowFunc;
- 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,
+ List<RootDisplayArea> displayAreaGroupRoots,
+ @Nullable BiFunction<WindowToken, Bundle, RootDisplayArea>
+ selectRootForWindowFunc) {
+ super(wmService, root);
+ mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots);
+ mSelectRootForWindowFunc = selectRootForWindowFunc == null
+ // Always return the highest level root of the logical display when the func is
+ // not specified.
+ ? (window, options) -> mRoot
+ : selectRootForWindowFunc;
}
@Override
@@ -316,64 +528,39 @@
@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];
+ return mSelectRootForWindowFunc.apply(token, token.mOptions).findAreaForToken(token);
}
@VisibleForTesting
- ArrayList<Feature> getFeatures() {
- return mFeatures;
+ List<Feature> getFeatures() {
+ Set<Feature> features = new ArraySet<>();
+ features.addAll(mRoot.mFeatures);
+ for (int i = 0; i < mDisplayAreaGroupRoots.size(); i++) {
+ features.addAll(mDisplayAreaGroupRoots.get(i).mFeatures);
+ }
+ return new ArrayList<>(features);
}
@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);
+ List<DisplayArea<? extends WindowContainer>> displayAreas = new ArrayList<>();
+ getDisplayAreas(mRoot, featureId, displayAreas);
+ for (int i = 0; i < mDisplayAreaGroupRoots.size(); i++) {
+ getDisplayAreas(mDisplayAreaGroupRoots.get(i), featureId, displayAreas);
+ }
+ return displayAreas;
+ }
+
+ private static void getDisplayAreas(RootDisplayArea root, int featureId,
+ List<DisplayArea<? extends WindowContainer>> displayAreas) {
+ List<Feature> features = root.mFeatures;
+ for (int i = 0; i < features.size(); i++) {
+ Feature feature = features.get(i);
+ if (feature.mId == featureId) {
+ displayAreas.addAll(root.mFeatureToDisplayAreas.get(feature));
}
}
- return new ArrayList<>();
}
-
- public List<DisplayArea<? extends WindowContainer>> getDisplayAreas(Feature feature) {
- return mAreas.get(feature);
- }
-
- 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;
- }
- }
- }
-
- 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 {
@@ -382,11 +569,21 @@
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 +596,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 +615,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..0a3b884 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -71,6 +71,7 @@
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
@@ -231,7 +232,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;
@@ -910,7 +911,7 @@
* @param root {@link RootWindowContainer}
*/
DisplayContent(Display display, RootWindowContainer root) {
- super(root.mWindowManager);
+ super(root.mWindowManager, "DisplayContent", FEATURE_ROOT);
if (mWmService.mRoot.getDisplayContent(display.getDisplayId()) != null) {
throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
+ " already exists="
@@ -990,7 +991,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);
@@ -1171,6 +1171,8 @@
activity.onRemovedFromDisplay();
if (activity == mFixedRotationLaunchingApp) {
+ // Make sure the states of associated tokens are also cleared.
+ activity.finishFixedRotationTransform();
setFixedRotationLaunchingAppUnchecked(null);
}
}
@@ -1469,6 +1471,12 @@
// window was transferred ({@link #mSkipAppTransitionAnimation}).
return false;
}
+ if ((mAppTransition.getTransitFlags()
+ & WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) != 0) {
+ // The transition may be finished before keyguard hidden. In order to avoid the
+ // intermediate orientation change, it is more stable to freeze the display.
+ return false;
+ }
} else if (r != topRunningActivity()) {
// If the transition has not started yet, the activity must be the top.
return false;
@@ -2184,52 +2192,27 @@
* Returns the topmost stack on the display that is compatible with the input windowing mode and
* activity type. Null is no compatible stack on the display.
*/
+ @Nullable
ActivityStack getStack(int windowingMode, int activityType) {
- for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final ActivityStack stack = getTaskDisplayAreaAt(tdaNdx)
- .getStack(windowingMode, activityType);
- if (stack != null) {
- return stack;
- }
- }
- return null;
+ return getItemFromTaskDisplayAreas(taskDisplayArea ->
+ taskDisplayArea.getStack(windowingMode, activityType));
}
- protected int getTaskDisplayAreaCount() {
- return mDisplayAreaPolicy.getTaskDisplayAreaCount();
- }
-
- protected TaskDisplayArea getTaskDisplayAreaAt(int index) {
- return mDisplayAreaPolicy.getTaskDisplayAreaAt(index);
- }
-
+ @Nullable
ActivityStack getStack(int rootTaskId) {
- for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final ActivityStack stack = getTaskDisplayAreaAt(tdaNdx).getStack(rootTaskId);
- if (stack != null) {
- return stack;
- }
- }
- return null;
+ return getItemFromTaskDisplayAreas(taskDisplayArea ->
+ taskDisplayArea.getStack(rootTaskId));
}
protected int getStackCount() {
- int totalStackCount = 0;
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- totalStackCount += getTaskDisplayAreaAt(i).getStackCount();
- }
- return totalStackCount;
+ return reduceOnAllTaskDisplayAreas((taskDisplayArea, count) ->
+ count + taskDisplayArea.getStackCount(), 0 /* initValue */);
}
@VisibleForTesting
+ @Nullable
ActivityStack getTopStack() {
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getTaskDisplayAreaAt(i).getTopStack();
- if (stack != null) {
- return stack;
- }
- }
- return null;
+ return getItemFromTaskDisplayAreas(TaskDisplayArea::getTopStack);
}
/**
@@ -2292,12 +2275,23 @@
void onAppTransitionDone() {
super.onAppTransitionDone();
mWmService.mWindowsChanged = true;
+ // If the transition finished callback cannot match the token for some reason, make sure the
+ // rotated state is cleared if it is already invisible.
+ if (mFixedRotationLaunchingApp != null && !mFixedRotationLaunchingApp.mVisibleRequested
+ && !mFixedRotationLaunchingApp.isVisible()
+ && !mDisplayRotation.isRotatingSeamlessly()) {
+ clearFixedRotationLaunchingApp();
+ }
}
@Override
public void setWindowingMode(int windowingMode) {
- super.setWindowingMode(windowingMode);
- super.setDisplayWindowingMode(windowingMode);
+ // Intentionally call onRequestedOverrideConfigurationChanged() directly to change windowing
+ // mode and display windowing mode atomically.
+ mTmpConfiguration.setTo(getRequestedOverrideConfiguration());
+ mTmpConfiguration.windowConfiguration.setWindowingMode(windowingMode);
+ mTmpConfiguration.windowConfiguration.setDisplayWindowingMode(windowingMode);
+ onRequestedOverrideConfigurationChanged(mTmpConfiguration);
}
@Override
@@ -2518,7 +2512,8 @@
* or for cases when multi-instance is not supported yet (like Split-screen, PiP or Recents).
*/
TaskDisplayArea getDefaultTaskDisplayArea() {
- return mDisplayAreaPolicy.getTaskDisplayAreaAt(0);
+ return getItemFromTaskDisplayAreas(taskDisplayArea -> taskDisplayArea,
+ false /* traverseTopToBottom */);
}
void positionDisplayAt(int position, boolean includingParents) {
@@ -2737,9 +2732,7 @@
}
void prepareFreezingTaskBounds() {
- for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- getTaskDisplayAreaAt(tdaNdx).prepareFreezingTaskBounds();
- }
+ forAllTaskDisplayAreas(TaskDisplayArea::prepareFreezingTaskBounds);
}
void rotateBounds(int oldRotation, int newRotation, Rect bounds) {
@@ -2934,9 +2927,9 @@
pw.println();
pw.println(prefix + "Task display areas in top down Z order:");
- for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- getTaskDisplayAreaAt(tdaNdx).dump(pw, prefix + " ", dumpAll);
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ taskDisplayArea.dump(pw, prefix + " ", dumpAll);
+ });
pw.println();
if (!mExitingTokens.isEmpty()) {
@@ -2953,11 +2946,9 @@
final ScreenRotationAnimation rotationAnimation = getRotationAnimation();
if (rotationAnimation != null) {
- pw.print(subPrefix);
pw.println(" mScreenRotationAnimation:");
- rotationAnimation.printTo(" ", pw);
+ rotationAnimation.printTo(subPrefix, pw);
} else if (dumpAll) {
- pw.print(subPrefix);
pw.println(" no ScreenRotationAnimation ");
}
@@ -3503,12 +3494,11 @@
}
}
- private void updateImeControlTarget() {
+ void updateImeControlTarget() {
mInputMethodControlTarget = computeImeControlTarget();
mInsetsStateController.onImeControlTargetChanged(mInputMethodControlTarget);
- final WindowState win = mInputMethodControlTarget != null
- ? mInputMethodControlTarget.getWindow() : null;
+ final WindowState win = InsetsControlTarget.asWindowOrNull(mInputMethodControlTarget);
final IBinder token = win != null ? win.mClient.asBinder() : null;
// Note: not allowed to call into IMMS with the WM lock held, hence the post.
mWmService.mH.post(() ->
@@ -3532,6 +3522,17 @@
if (!isImeControlledByApp() && mRemoteInsetsControlTarget != null) {
return mRemoteInsetsControlTarget;
} else {
+ // Now, a special case -- if the last target's window is in the process of exiting, but
+ // not removed, keep on the last target to avoid IME flicker.
+ final WindowState cur = InsetsControlTarget.asWindowOrNull(mInputMethodControlTarget);
+ if (cur != null && !cur.mRemoved && cur.isDisplayedLw() && cur.isClosing()
+ && !cur.isActivityTypeHome()) {
+ if (DEBUG_INPUT_METHOD) {
+ Slog.v(TAG_WM, "Not changing control while current window"
+ + " is closing and not removed");
+ }
+ return cur;
+ }
// Otherwise, we just use the ime target as received from IME.
return mInputMethodInputTarget;
}
@@ -3648,15 +3649,15 @@
drawnWindowTypes.put(TYPE_NOTIFICATION_SHADE, true);
final WindowState visibleNotDrawnWindow = getWindow(w -> {
- boolean isVisible = w.mViewVisibility == View.VISIBLE && !w.mObscured;
- boolean hasDrawn = w.isDrawnLw() && w.hasDrawnLw();
- if (isVisible && !hasDrawn) {
+ final boolean isVisible = w.isVisible() && !w.mObscured;
+ final boolean isDrawn = w.isDrawnLw();
+ if (isVisible && !isDrawn) {
ProtoLog.d(WM_DEBUG_BOOT,
"DisplayContent: boot is waiting for window of type %d to be drawn",
w.mAttrs.type);
return true;
}
- if (hasDrawn) {
+ if (isDrawn) {
switch (w.mAttrs.type) {
case TYPE_BOOT_PROGRESS:
case TYPE_BASE_APPLICATION:
@@ -4110,9 +4111,9 @@
}
// Initialize state of exiting applications.
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- getTaskDisplayAreaAt(i).setExitingTokensHasVisible(hasVisible);
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ taskDisplayArea.setExitingTokensHasVisible(hasVisible);
+ });
}
void removeExistingTokensIfPossible() {
@@ -4124,9 +4125,7 @@
}
// Time to remove any exiting applications?
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- getTaskDisplayAreaAt(i).removeExistingAppTokensIfPossible();
- }
+ forAllTaskDisplayAreas(TaskDisplayArea::removeExistingAppTokensIfPossible);
}
@Override
@@ -4450,9 +4449,9 @@
}
void assignStackOrdering() {
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- getTaskDisplayAreaAt(i).assignStackOrdering(getPendingTransaction());
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ taskDisplayArea.assignStackOrdering(getPendingTransaction());
+ });
}
/**
@@ -4994,13 +4993,7 @@
@Nullable
ActivityStack getFocusedStack() {
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getTaskDisplayAreaAt(i).getFocusedStack();
- if (stack != null) {
- return stack;
- }
- }
- return null;
+ return getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedStack);
}
/**
@@ -5008,15 +5001,15 @@
* ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
*/
void removeStacksInWindowingModes(int... windowingModes) {
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- getTaskDisplayAreaAt(i).removeStacksInWindowingModes(windowingModes);
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ taskDisplayArea.removeStacksInWindowingModes(windowingModes);
+ });
}
void removeStacksWithActivityTypes(int... activityTypes) {
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- getTaskDisplayAreaAt(i).removeStacksWithActivityTypes(activityTypes);
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ taskDisplayArea.removeStacksWithActivityTypes(activityTypes);
+ });
}
ActivityRecord topRunningActivity() {
@@ -5032,15 +5025,10 @@
* can be shown on top of the keyguard will be considered.
* @return The top running activity. {@code null} if none is available.
*/
+ @Nullable
ActivityRecord topRunningActivity(boolean considerKeyguardState) {
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- final ActivityRecord activity = getTaskDisplayAreaAt(i)
- .topRunningActivity(considerKeyguardState);
- if (activity != null) {
- return activity;
- }
- }
- return null;
+ return getItemFromTaskDisplayAreas(taskDisplayArea ->
+ taskDisplayArea.topRunningActivity(considerKeyguardState));
}
boolean updateDisplayOverrideConfigurationLocked() {
@@ -5201,18 +5189,17 @@
void remove() {
mRemoving = true;
- ActivityStack lastReparentedStack = null;
+ ActivityStack lastReparentedStack;
mRootWindowContainer.mStackSupervisor.beginDeferResume();
try {
- int numTaskContainers = getTaskDisplayAreaCount();
- for (int tdaNdx = 0; tdaNdx < numTaskContainers; tdaNdx++) {
- final ActivityStack lastReparentedStackFromArea = getTaskDisplayAreaAt(tdaNdx)
- .remove();
+ lastReparentedStack = reduceOnAllTaskDisplayAreas((taskDisplayArea, stack) -> {
+ final ActivityStack lastReparentedStackFromArea = taskDisplayArea.remove();
if (lastReparentedStackFromArea != null) {
- lastReparentedStack = lastReparentedStackFromArea;
+ return lastReparentedStackFromArea;
}
- }
+ return stack;
+ }, null /* initValue */, false /* traverseTopToBottom */);
} finally {
mRootWindowContainer.mStackSupervisor.endDeferResume();
}
@@ -5239,26 +5226,19 @@
}
// Check if all task display areas have only the empty home stacks left.
- boolean onlyEmptyHomeStacksLeft = true;
- for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = getTaskDisplayAreaAt(tdaNdx);
+ boolean hasNonEmptyHomeStack = forAllTaskDisplayAreas(taskDisplayArea -> {
if (taskDisplayArea.getStackCount() != 1) {
- onlyEmptyHomeStacksLeft = false;
- break;
+ return true;
}
final ActivityStack stack = taskDisplayArea.getStackAt(0);
- if (!stack.isActivityTypeHome() || stack.hasChild()) {
- onlyEmptyHomeStacksLeft = false;
- break;
- }
- }
- if (onlyEmptyHomeStacksLeft) {
+ return !stack.isActivityTypeHome() || stack.hasChild();
+ });
+ if (!hasNonEmptyHomeStack) {
// Release this display if only empty home stack(s) are left. This display will be
// released along with the stack(s) removal.
- for (int tdaNdx = getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final ActivityStack s = getTaskDisplayAreaAt(tdaNdx).getStackAt(0);
- s.removeIfPossible();
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ taskDisplayArea.getStackAt(0).removeIfPossible();
+ });
} else if (getTopStack() == null) {
removeIfPossible();
mRootWindowContainer.mStackSupervisor
@@ -5323,10 +5303,10 @@
}
mInEnsureActivitiesVisible = true;
try {
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- getTaskDisplayAreaAt(i).ensureActivitiesVisible(starting, configChanges,
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ taskDisplayArea.ensureActivitiesVisible(starting, configChanges,
preserveWindows, notifyClients);
- }
+ });
} finally {
mInEnsureActivitiesVisible = false;
}
@@ -5341,8 +5321,9 @@
}
void setDisplayToSingleTaskInstance() {
- final int taskDisplayAreaCount = getTaskDisplayAreaCount();
- if (taskDisplayAreaCount > 1) {
+ int tdaCount = reduceOnAllTaskDisplayAreas((taskDisplayArea, count) -> ++count,
+ 0 /* initValue */);
+ if (tdaCount > 1) {
throw new IllegalArgumentException(
"Display already has multiple task display areas. display=" + this);
}
@@ -5565,6 +5546,19 @@
mRemoteInsetsController = controller;
}
+ /**
+ * Notifies the remote insets controller that the top focused window has changed.
+ *
+ * @param packageName The name of the package that is open in the top focused window.
+ */
+ void topFocusedWindowChanged(String packageName) {
+ try {
+ mRemoteInsetsController.topFocusedWindowChanged(packageName);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to deliver package in top focused window change", e);
+ }
+ }
+
void notifyInsetsChanged() {
try {
mRemoteInsetsController.insetsChanged(
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 44dc687..d2f5d2d 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -386,11 +386,6 @@
private int mForcingShowNavBarLayer;
private boolean mForceShowSystemBars;
- /**
- * Force the display of system bars regardless of other settings.
- */
- private boolean mForceShowSystemBarsFromExternal;
-
private boolean mShowingDream;
private boolean mLastShowingDream;
private boolean mDreamingLockscreen;
@@ -480,7 +475,6 @@
final Resources r = mContext.getResources();
mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer);
mDeskDockEnablesAccelerometer = r.getBoolean(R.bool.config_deskDockEnablesAccelerometer);
- mForceShowSystemBarsFromExternal = r.getBoolean(R.bool.config_forceShowSystemBars);
mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(
Context.ACCESSIBILITY_SERVICE);
@@ -698,17 +692,6 @@
return mDockMode;
}
- /**
- * @see WindowManagerService.setForceShowSystemBars
- */
- void setForceShowSystemBars(boolean forceShowSystemBars) {
- mForceShowSystemBarsFromExternal = forceShowSystemBars;
- }
-
- boolean getForceShowSystemBars() {
- return mForceShowSystemBarsFromExternal;
- }
-
public boolean hasNavigationBar() {
return mHasNavigationBar;
}
@@ -3299,16 +3282,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);
@@ -3549,8 +3522,7 @@
// We need to force system bars when the docked stack is visible, when the freeform stack
// is focused but also when we are resizing for the transitions when docked stack
// visibility changes.
- mForceShowSystemBars = dockedStackVisible || win.inFreeformWindowingMode() || resizing
- || mForceShowSystemBarsFromExternal;
+ mForceShowSystemBars = dockedStackVisible || win.inFreeformWindowingMode() || resizing;
final boolean forceOpaqueStatusBar = mForceShowSystemBars && !isKeyguardShowing();
// apply translucent bar vis flags
@@ -3925,8 +3897,8 @@
}
pw.print(prefix); pw.print("mTopIsFullscreen="); pw.println(mTopIsFullscreen);
pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar);
- pw.print(prefix); pw.print("mForceShowSystemBarsFromExternal=");
- pw.print(mForceShowSystemBarsFromExternal);
+ pw.print(prefix); pw.print("mRemoteInsetsControllerControlsSystemBars");
+ pw.print(mDisplayContent.getInsetsPolicy().getRemoteInsetsControllerControlsSystemBars());
pw.print(" mAllowLockscreenWhenOn="); pw.println(mAllowLockscreenWhenOn);
mStatusBarController.dump(pw, prefix);
mNavigationBarController.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 8298763..99ee5e1 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -18,13 +18,14 @@
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_IME;
-import android.graphics.PixelFormat;
import android.view.InsetsSource;
import android.view.WindowInsets;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.protolog.common.ProtoLog;
+import java.io.PrintWriter;
+
/**
* Controller for IME inset source on the server. It's called provider as it provides the
* {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}.
@@ -132,8 +133,17 @@
|| (mImeTargetFromIme != null && dcTarget.getParentWindow() == mImeTargetFromIme
&& dcTarget.mSubLayer > mImeTargetFromIme.mSubLayer)
|| mImeTargetFromIme == mDisplayContent.getImeFallback()
- // If IME target is transparent but control target matches requesting window.
- || (controlTarget == mImeTargetFromIme
- && PixelFormat.formatHasAlpha(dcTarget.mAttrs.format));
+ || (!mImeTargetFromIme.isClosing() && controlTarget == mImeTargetFromIme);
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ super.dump(pw, prefix);
+ if (mImeTargetFromIme != null) {
+ pw.print(prefix);
+ pw.print("showImePostLayout pending for mImeTargetFromIme=");
+ pw.print(mImeTargetFromIme);
+ pw.println();
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 8734b5e..0216db4 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -16,13 +16,14 @@
package com.android.server.wm;
-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 +478,18 @@
mService.getRecentsAnimationController();
final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null
&& recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord);
+ final int type = w.mAttrs.type;
+ final boolean isVisible = w.isVisibleLw();
if (inputChannel == null || inputWindowHandle == null || w.mRemoved
|| (w.cantReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) {
if (w.mWinAnimator.hasSurface()) {
+ // Assign an InputInfo with type to the overlay window which can't receive input
+ // event. This is used to omit Surfaces from occlusion detection.
+ populateOverlayInputInfo(mInvalidInputWindow, w.getName(), type, isVisible);
mInputTransaction.setInputWindowInfo(
- w.mWinAnimator.mSurfaceController.getClientViewRootSurface(),
- mInvalidInputWindow);
+ w.mWinAnimator.mSurfaceController.getClientViewRootSurface(),
+ mInvalidInputWindow);
+ return;
}
// Skip this window because it cannot possibly receive input.
return;
@@ -490,9 +497,7 @@
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();
if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
if (recentsAnimationController.updateInputConsumerForApp(
@@ -555,6 +560,26 @@
}
}
+ // 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.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 +589,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/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java
index c50f296..3ffc26a 100644
--- a/services/core/java/com/android/server/wm/InsetsControlTarget.java
+++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java
@@ -62,4 +62,8 @@
return false;
}
+ /** Returns {@code target.getWindow()}, or null if {@code target} is {@code null}. */
+ static WindowState asWindowOrNull(InsetsControlTarget target) {
+ return target != null ? target.getWindow() : null;
+ }
}
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 254356d..b7287e7 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -46,7 +46,9 @@
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Bounds;
import android.view.WindowInsetsAnimationControlListener;
+import android.view.WindowManager;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.DisplayThread;
@@ -97,12 +99,30 @@
private BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR);
private BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR);
private boolean mAnimatingShown;
+ /**
+ * Let remote insets controller control system bars regardless of other settings.
+ */
+ private boolean mRemoteInsetsControllerControlsSystemBars;
private final float[] mTmpFloat9 = new float[9];
InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) {
mStateController = stateController;
mDisplayContent = displayContent;
mPolicy = displayContent.getDisplayPolicy();
+ mRemoteInsetsControllerControlsSystemBars = mPolicy.getContext().getResources().getBoolean(
+ R.bool.config_remoteInsetsControllerControlsSystemBars);
+ }
+
+ boolean getRemoteInsetsControllerControlsSystemBars() {
+ return mRemoteInsetsControllerControlsSystemBars;
+ }
+
+ /**
+ * Used only for testing.
+ */
+ @VisibleForTesting
+ void setRemoteInsetsControllerControlsSystemBars(boolean controlsSystemBars) {
+ mRemoteInsetsControllerControlsSystemBars = controlsSystemBars;
}
/** Updates the target which can control system bars. */
@@ -256,6 +276,11 @@
// Notification shade has control anyways, no reason to force anything.
return focusedWin;
}
+ if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
+ mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
+ focusedWin.mAttrs.packageName);
+ return mDisplayContent.mRemoteInsetsControlTarget;
+ }
if (forceShowsSystemBarsForWindowingMode) {
// Status bar is forcibly shown for the windowing mode which is a steady state.
// We don't want the client to control the status bar, and we will dispatch the real
@@ -285,6 +310,11 @@
// Notification shade has control anyways, no reason to force anything.
return focusedWin;
}
+ if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
+ mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
+ focusedWin.mAttrs.packageName);
+ return mDisplayContent.mRemoteInsetsControlTarget;
+ }
if (forceShowsSystemBarsForWindowingMode) {
// Navigation bar is forcibly shown for the windowing mode which is a steady state.
// We don't want the client to control the navigation bar, and we will dispatch the real
@@ -300,6 +330,28 @@
return focusedWin;
}
+ /**
+ * Determines whether the remote insets controller should take control of system bars for all
+ * windows.
+ */
+ boolean remoteInsetsControllerControlsSystemBars(@Nullable WindowState focusedWin) {
+ if (focusedWin == null) {
+ return false;
+ }
+ if (!mRemoteInsetsControllerControlsSystemBars) {
+ return false;
+ }
+ if (mDisplayContent == null || mDisplayContent.mRemoteInsetsControlTarget == null) {
+ // No remote insets control target to take control of insets.
+ return false;
+ }
+ // If necessary, auto can control application windows when
+ // config_remoteInsetsControllerControlsSystemBars is set to true. This is useful in cases
+ // where we want to dictate system bar inset state for applications.
+ return focusedWin.getAttrs().type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
+ && focusedWin.getAttrs().type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+ }
+
private boolean forceShowsStatusBarTransiently() {
final WindowState win = mPolicy.getStatusBar();
return win != null && (win.mAttrs.privateFlags & PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR) != 0;
@@ -321,10 +373,7 @@
// We need to force system bars when the docked stack is visible, when the freeform stack
// is visible but also when we are resizing for the transitions when docked stack
// visibility changes.
- return isDockedStackVisible
- || isFreeformStackVisible
- || isResizing
- || mPolicy.getForceShowSystemBars();
+ return isDockedStackVisible || isFreeformStackVisible || isResizing;
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 63083fa..9c978fd 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -19,6 +19,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_INVALID;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
@@ -44,6 +45,7 @@
import android.view.InsetsState.InternalInsetsType;
import android.view.WindowManager;
+import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.protolog.common.ProtoLog;
import java.io.PrintWriter;
@@ -74,7 +76,21 @@
w.notifyInsetsChanged();
}
};
- private final InsetsControlTarget mEmptyImeControlTarget = new InsetsControlTarget() { };
+ private final InsetsControlTarget mEmptyImeControlTarget = new InsetsControlTarget() {
+ @Override
+ public void notifyInsetsControlChanged() {
+ InsetsSourceControl[] controls = getControlsForDispatch(this);
+ if (controls == null) {
+ return;
+ }
+ for (InsetsSourceControl control : controls) {
+ if (control.getType() == ITYPE_IME) {
+ mDisplayContent.mWmService.mH.post(() ->
+ InputMethodManagerInternal.get().removeImeSurface());
+ }
+ }
+ }
+ };
InsetsStateController(DisplayContent displayContent) {
mDisplayContent = displayContent;
@@ -171,6 +187,7 @@
state = new InsetsState(state);
state.removeSource(ITYPE_STATUS_BAR);
state.removeSource(ITYPE_NAVIGATION_BAR);
+ state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
}
if (aboveIme) {
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 4c10d581..76f2365 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -40,6 +40,7 @@
import static com.android.server.wm.KeyguardOccludedProto.DISPLAY_ID;
import static com.android.server.wm.KeyguardOccludedProto.KEYGUARD_OCCLUDED;
+import android.annotation.Nullable;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
@@ -133,10 +134,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;
}
@@ -513,9 +515,9 @@
* Only the top non-pinned activity of the focusable stack on each display can control its
* occlusion state.
*/
+ @Nullable
private ActivityStack getStackForControllingOccluding(DisplayContent display) {
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+ return display.getItemFromTaskDisplayAreas(taskDisplayArea -> {
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
if (stack != null && stack.isFocusableAndVisible()
@@ -523,8 +525,8 @@
return stack;
}
}
- }
- return null;
+ return null;
+ });
}
void dumpStatus(PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index c7f7834..c255a18 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -359,17 +359,20 @@
RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds,
Rect endBounds, Rect startBounds) {
mWindowContainer = windowContainer;
- mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds);
if (startBounds != null) {
mStartBounds = new Rect(startBounds);
+ mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
+ mStartBounds);
mTmpRect.set(startBounds);
mTmpRect.offsetTo(0, 0);
if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) {
mThumbnailAdapter =
new RemoteAnimationAdapterWrapper(this, new Point(0, 0), localBounds,
- mTmpRect);
+ mTmpRect, new Rect());
}
} else {
+ mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
+ new Rect(endPos.x, endPos.y, endBounds.right, endBounds.bottom));
mStartBounds = null;
}
}
@@ -407,13 +410,15 @@
final Point mPosition = new Point();
final Rect mLocalBounds;
final Rect mStackBounds = new Rect();
+ final Rect mStartBounds = new Rect();
RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position,
- Rect localBounds, Rect stackBounds) {
+ Rect localBounds, Rect stackBounds, Rect startBounds) {
mRecord = record;
mPosition.set(position.x, position.y);
mLocalBounds = localBounds;
mStackBounds.set(stackBounds);
+ mStartBounds.set(startBounds);
}
@Override
@@ -427,13 +432,12 @@
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation");
// Restore position and stack crop until client has a chance to modify it.
- if (mRecord.mStartBounds != null) {
- t.setPosition(animationLeash, mRecord.mStartBounds.left, mRecord.mStartBounds.top);
- t.setWindowCrop(animationLeash, mRecord.mStartBounds.width(),
- mRecord.mStartBounds.height());
+ if (mStartBounds.isEmpty()) {
+ t.setPosition(animationLeash, 0, 0);
+ t.setWindowCrop(animationLeash, -1, -1);
} else {
- t.setPosition(animationLeash, mPosition.x, mPosition.y);
- t.setWindowCrop(animationLeash, mStackBounds.width(), mStackBounds.height());
+ t.setPosition(animationLeash, mStartBounds.left, mStartBounds.top);
+ t.setWindowCrop(animationLeash, mStartBounds.width(), mStartBounds.height());
}
mCapturedLeash = animationLeash;
mCapturedFinishCallback = finishCallback;
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..faed7fa
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RootDisplayArea.java
@@ -0,0 +1,76 @@
+/*
+ * 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 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;
+
+ /** Whether the hierarchy has been built. */
+ private boolean mHasBuiltHierarchy;
+
+ RootDisplayArea(WindowManagerService wms, String name, int featureId) {
+ super(wms, Type.ANY, name, featureId);
+ }
+
+ /** 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) {
+ if (mHasBuiltHierarchy) {
+ throw new IllegalStateException("Root should only build the hierarchy once");
+ }
+ mHasBuiltHierarchy = true;
+ mFeatures = Collections.unmodifiableList(features);
+ mAreaForLayer = areaForLayer;
+ mFeatureToDisplayAreas = featureToDisplayAreas;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index e787aac..40f8fab 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1106,9 +1106,9 @@
boolean displayHasContent = false;
ProtoLog.d(WM_DEBUG_KEEP_SCREEN_ON,
- "handleNotObscuredLocked w: %s, w.mHasSurface: %b, w.isOnScreen(): %b, w"
- + ".isDisplayedLw(): %b, w.mAttrs.userActivityTimeout: %d",
- w, w.mHasSurface, onScreen, w.isDisplayedLw(), w.mAttrs.userActivityTimeout);
+ "handleNotObscuredLocked w: %s, w.mHasSurface: %b, w.isOnScreen(): %b, w"
+ + ".isDisplayedLw(): %b, w.mAttrs.userActivityTimeout: %d",
+ w, w.mHasSurface, onScreen, w.isDisplayedLw(), w.mAttrs.userActivityTimeout);
if (w.mHasSurface && onScreen) {
if (!syswin && w.mAttrs.userActivityTimeout >= 0 && mUserActivityTimeout < 0) {
mUserActivityTimeout = w.mAttrs.userActivityTimeout;
@@ -1457,16 +1457,12 @@
}
void startHomeOnEmptyDisplays(String reason) {
- for (int i = getChildCount() - 1; i >= 0; i--) {
- final DisplayContent display = getChildAt(i);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- if (taskDisplayArea.topRunningActivity() == null) {
- startHomeOnTaskDisplayArea(mCurrentUser, reason, taskDisplayArea,
- false /* allowInstrumenting */, false /* fromHomeKey */);
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ if (taskDisplayArea.topRunningActivity() == null) {
+ startHomeOnTaskDisplayArea(mCurrentUser, reason, taskDisplayArea,
+ false /* allowInstrumenting */, false /* fromHomeKey */);
}
- }
+ });
}
boolean startHomeOnDisplay(int userId, String reason, int displayId) {
@@ -1483,13 +1479,10 @@
}
final DisplayContent display = getDisplayContent(displayId);
- boolean result = false;
- for (int tcNdx = display.getTaskDisplayAreaCount() - 1; tcNdx >= 0; --tcNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tcNdx);
- result |= startHomeOnTaskDisplayArea(userId, reason, taskDisplayArea,
- allowInstrumenting, fromHomeKey);
- }
- return result;
+ return display.reduceOnAllTaskDisplayAreas((taskDisplayArea, result) ->
+ result | startHomeOnTaskDisplayArea(userId, reason, taskDisplayArea,
+ allowInstrumenting, fromHomeKey),
+ false /* initValue */);
}
/**
@@ -1826,31 +1819,27 @@
final ArrayList<IBinder> topActivityTokens = new ArrayList<>();
final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
// Traverse all displays.
- for (int dNdx = getChildCount() - 1; dNdx >= 0; dNdx--) {
- final DisplayContent display = getChildAt(dNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea =
- display.getTaskDisplayAreaAt(tdaNdx);
- // Traverse all stacks on a display area.
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- // Get top activity from a visible stack and add it to the list.
- if (stack.shouldBeVisible(null /* starting */)) {
- final ActivityRecord top = stack.getTopNonFinishingActivity();
- if (top != null) {
- if (stack == topFocusedStack) {
- topActivityTokens.add(0, top.appToken);
- } else {
- topActivityTokens.add(top.appToken);
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ // Traverse all stacks on a display area.
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+ // Get top activity from a visible stack and add it to the list.
+ if (stack.shouldBeVisible(null /* starting */)) {
+ final ActivityRecord top = stack.getTopNonFinishingActivity();
+ if (top != null) {
+ if (stack == topFocusedStack) {
+ topActivityTokens.add(0, top.appToken);
+ } else {
+ topActivityTokens.add(top.appToken);
}
}
}
}
- }
+ });
return topActivityTokens;
}
+ @Nullable
ActivityStack getTopDisplayFocusedStack() {
for (int i = getChildCount() - 1; i >= 0; --i) {
final ActivityStack focusedStack = getChildAt(i).getFocusedStack();
@@ -1861,6 +1850,7 @@
return null;
}
+ @Nullable
ActivityRecord getTopResumedActivity() {
final ActivityStack focusedStack = getTopDisplayFocusedStack();
if (focusedStack == null) {
@@ -1872,18 +1862,7 @@
}
// The top focused stack might not have a resumed activity yet - look on all displays in
// focus order.
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final DisplayContent display = getChildAt(i);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- final ActivityRecord resumedActivityOnTaskContainer = taskDisplayArea
- .getFocusedActivity();
- if (resumedActivityOnTaskContainer != null) {
- return resumedActivityOnTaskContainer;
- }
- }
- }
- return null;
+ return getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedActivity);
}
boolean isTopDisplayFocusedStack(ActivityStack stack) {
@@ -1897,25 +1876,21 @@
// First, found out what is currently the foreground app, so that we don't blow away the
// previous app if this activity is being hosted by the process that is actually still the
// foreground.
- WindowProcessController fgApp = null;
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- if (isTopDisplayFocusedStack(stack)) {
- final ActivityRecord resumedActivity = stack.getResumedActivity();
- if (resumedActivity != null) {
- fgApp = resumedActivity.app;
- } else if (stack.mPausingActivity != null) {
- fgApp = stack.mPausingActivity.app;
- }
- break;
+ WindowProcessController fgApp = reduceOnAllTaskDisplayAreas((taskDisplayArea, app) -> {
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+ if (isTopDisplayFocusedStack(stack)) {
+ final ActivityRecord resumedActivity = stack.getResumedActivity();
+ if (resumedActivity != null) {
+ app = resumedActivity.app;
+ } else if (stack.mPausingActivity != null) {
+ app = stack.mPausingActivity.app;
}
+ break;
}
}
- }
+ return app;
+ }, null /* initValue */);
// Now set this one as the previous process, only if that really makes sense to.
if (r.hasProcess() && fgApp != null && r.app != fgApp
@@ -2024,16 +1999,12 @@
mCurrentUser = userId;
mStackSupervisor.mStartingUsers.add(uss);
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- stack.switchUser(userId);
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+ stack.switchUser(userId);
}
- }
+ });
final int restoreStackId = mUserStackInFront.get(userId);
ActivityStack stack = getStack(restoreStackId);
@@ -2221,6 +2192,7 @@
}
}
+ @Nullable
ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
mTmpFindTaskResult.clear();
@@ -2234,20 +2206,20 @@
}
}
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- if (taskDisplayArea == preferredTaskDisplayArea) {
- continue;
- }
-
- taskDisplayArea.findTaskLocked(r, false /* isPreferredDisplay */,
- mTmpFindTaskResult);
- if (mTmpFindTaskResult.mIdealMatch) {
- return mTmpFindTaskResult.mRecord;
- }
+ final ActivityRecord task = getItemFromTaskDisplayAreas(taskDisplayArea -> {
+ if (taskDisplayArea == preferredTaskDisplayArea) {
+ return null;
}
+
+ taskDisplayArea.findTaskLocked(r, false /* isPreferredDisplay */,
+ mTmpFindTaskResult);
+ if (mTmpFindTaskResult.mIdealMatch) {
+ return mTmpFindTaskResult.mRecord;
+ }
+ return null;
+ });
+ if (task != null) {
+ return task;
}
if (DEBUG_TASKS && mTmpFindTaskResult.mRecord == null) Slog.d(TAG_TASKS, "No task found");
@@ -2261,24 +2233,20 @@
* @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished.
*/
int finishTopCrashedActivities(WindowProcessController app, String reason) {
- Task finishedTask = null;
ActivityStack focusedStack = getTopDisplayFocusedStack();
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- // It is possible that request to finish activity might also remove its task and
- // stack, so we need to be careful with indexes in the loop and check child count
- // every time.
- for (int stackNdx = 0; stackNdx < taskDisplayArea.getStackCount(); ++stackNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(stackNdx);
- final Task t = stack.finishTopCrashedActivityLocked(app, reason);
- if (stack == focusedStack || finishedTask == null) {
- finishedTask = t;
- }
+ Task finishedTask = reduceOnAllTaskDisplayAreas((taskDisplayArea, task) -> {
+ // It is possible that request to finish activity might also remove its task and
+ // stack, so we need to be careful with indexes in the loop and check child count
+ // every time.
+ for (int stackNdx = 0; stackNdx < taskDisplayArea.getStackCount(); ++stackNdx) {
+ final ActivityStack stack = taskDisplayArea.getStackAt(stackNdx);
+ final Task t = stack.finishTopCrashedActivityLocked(app, reason);
+ if (stack == focusedStack || task == null) {
+ task = t;
}
}
- }
+ return task;
+ }, null /* initValue */);
return finishedTask != null ? finishedTask.mTaskId : INVALID_TASK_ID;
}
@@ -2300,33 +2268,36 @@
}
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- boolean resumedOnDisplay = false;
final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- final ActivityRecord topRunningActivity = stack.topRunningActivity();
- if (!stack.isFocusableAndVisible() || topRunningActivity == null) {
- continue;
- }
- if (stack == targetStack) {
- // Simply update the result for targetStack because the targetStack had
- // already resumed in above. We don't want to resume it again, especially in
- // some cases, it would cause a second launch failure if app process was
- // dead.
- resumedOnDisplay |= result;
- continue;
- }
- if (taskDisplayArea.isTopStack(stack) && topRunningActivity.isState(RESUMED)) {
- // Kick off any lingering app transitions form the MoveTaskToFront
- // operation, but only consider the top task and stack on that display.
- stack.executeAppTransition(targetOptions);
- } else {
- resumedOnDisplay |= topRunningActivity.makeActiveIfNeeded(target);
- }
- }
- }
+ final boolean curResult = result;
+ boolean resumedOnDisplay = display.reduceOnAllTaskDisplayAreas(
+ (taskDisplayArea, resumed) -> {
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+ final ActivityRecord topRunningActivity = stack.topRunningActivity();
+ if (!stack.isFocusableAndVisible() || topRunningActivity == null) {
+ continue;
+ }
+ if (stack == targetStack) {
+ // Simply update the result for targetStack because the targetStack
+ // had already resumed in above. We don't want to resume it again,
+ // especially in some cases, it would cause a second launch failure
+ // if app process was dead.
+ resumed |= curResult;
+ continue;
+ }
+ if (taskDisplayArea.isTopStack(stack)
+ && topRunningActivity.isState(RESUMED)) {
+ // Kick off any lingering app transitions form the MoveTaskToFront
+ // operation, but only consider the top task and stack on that
+ // display.
+ stack.executeAppTransition(targetOptions);
+ } else {
+ resumed |= topRunningActivity.makeActiveIfNeeded(target);
+ }
+ }
+ return resumed;
+ }, false /* initValue */);
if (!resumedOnDisplay) {
// In cases when there are no valid activities (e.g. device just booted or launcher
// crashed) it's possible that nothing was resumed on a display. Requesting resume
@@ -2360,8 +2331,7 @@
}
// Set the sleeping state of the stacks on the display.
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+ display.forAllTaskDisplayAreas(taskDisplayArea -> {
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
if (displayShouldSleep) {
@@ -2375,7 +2345,8 @@
// triggered after contents are drawn on the display.
if (display.isSingleTaskInstance()) {
display.mDisplayContent.prepareAppTransition(
- TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false);
+ TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false,
+ 0 /* flags */, true /* forceOverride*/);
}
stack.awakeFromSleepingLocked();
if (display.isSingleTaskInstance()) {
@@ -2391,9 +2362,15 @@
// activity here.
resumeFocusedStacksTopActivities();
}
+ // The visibility update must not be called before resuming the top, so the
+ // display orientation can be updated first if needed. Otherwise there may
+ // have redundant configuration changes due to apply outdated display
+ // orientation (from keyguard) to activity.
+ stack.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ false /* preserveWindows */);
}
}
- }
+ });
}
}
@@ -2494,29 +2471,24 @@
ArrayList<ActivityManager.StackInfo> getAllStackInfos(int displayId) {
ArrayList<ActivityManager.StackInfo> list = new ArrayList<>();
if (displayId == INVALID_DISPLAY) {
- for (int displayNdx = 0; displayNdx < getChildCount(); ++displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- list.add(getStackInfo(stack));
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+ list.add(getStackInfo(stack));
}
- }
+ });
return list;
}
final DisplayContent display = getDisplayContent(displayId);
if (display == null) {
return list;
}
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+ display.forAllTaskDisplayAreas(taskDisplayArea -> {
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
list.add(getStackInfo(stack));
}
- }
+ });
return list;
}
@@ -2717,28 +2689,23 @@
// Tries to put all activity stacks to sleep. Returns true if all stacks were
// successfully put to sleep.
boolean putStacksToSleep(boolean allowDelay, boolean shuttingDown) {
- boolean allSleep = true;
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- // Stacks and activities could be removed while putting activities to sleep if
- // the app process was gone. This prevents us getting exception by accessing an
- // invalid stack index.
- if (sNdx >= taskDisplayArea.getStackCount()) {
- continue;
- }
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- if (allowDelay) {
- allSleep &= stack.goToSleepIfPossible(shuttingDown);
- } else {
- stack.goToSleep();
- }
+ return reduceOnAllTaskDisplayAreas((taskDisplayArea, result) -> {
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ // Stacks and activities could be removed while putting activities to sleep if
+ // the app process was gone. This prevents us getting exception by accessing an
+ // invalid stack index.
+ if (sNdx >= taskDisplayArea.getStackCount()) {
+ continue;
+ }
+ final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+ if (allowDelay) {
+ result &= stack.goToSleepIfPossible(shuttingDown);
+ } else {
+ stack.goToSleep();
}
}
- }
- return allSleep;
+ return result;
+ }, true /* initValue */);
}
void handleAppCrash(WindowProcessController app) {
@@ -3106,18 +3073,13 @@
}
boolean handleAppDied(WindowProcessController app) {
- boolean hasVisibleActivities = false;
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- hasVisibleActivities |= stack.handleAppDied(app);
- }
+ return reduceOnAllTaskDisplayAreas((taskDisplayArea, result) -> {
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+ result |= stack.handleAppDied(app);
}
- }
- return hasVisibleActivities;
+ return result;
+ }, false /* initValue */);
}
void closeSystemDialogActivities(String reason) {
@@ -3234,18 +3196,13 @@
}
void finishVoiceTask(IVoiceInteractionSession session) {
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- int numTaskContainers = display.getTaskDisplayAreaCount();
- for (int tdaNdx = 0; tdaNdx < numTaskContainers; tdaNdx++) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- final int numStacks = display.getStackCount();
- for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(stackNdx);
- stack.finishVoiceTask(session);
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ final int numStacks = taskDisplayArea.getStackCount();
+ for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ final ActivityStack stack = taskDisplayArea.getStackAt(stackNdx);
+ stack.finishVoiceTask(session);
}
- }
+ });
}
/**
@@ -3304,48 +3261,50 @@
}
boolean allResumedActivitiesVisible() {
- boolean foundResumed = false;
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- final ActivityRecord r = stack.getResumedActivity();
- if (r != null) {
- if (!r.nowVisible) {
- return false;
+ boolean[] foundResumed = {false};
+ final boolean foundInvisibleResumedActivity = forAllTaskDisplayAreas(
+ taskDisplayArea -> {
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+ final ActivityRecord r = stack.getResumedActivity();
+ if (r != null) {
+ if (!r.nowVisible) {
+ return true;
+ }
+ foundResumed[0] = true;
}
- foundResumed = true;
}
- }
- }
+ return false;
+ });
+ if (foundInvisibleResumedActivity) {
+ return false;
}
- return foundResumed;
+ return foundResumed[0];
}
boolean allPausedActivitiesComplete() {
- boolean pausing = true;
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- final ActivityRecord r = stack.mPausingActivity;
- if (r != null && !r.isState(PAUSED, STOPPED, STOPPING, FINISHING)) {
- if (DEBUG_STATES) {
- Slog.d(TAG_STATES, "allPausedActivitiesComplete: r=" + r
- + " state=" + r.getState());
- pausing = false;
- } else {
- return false;
+ boolean[] pausing = {true};
+ final boolean hasActivityNotCompleted = forAllTaskDisplayAreas(
+ taskDisplayArea -> {
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+ final ActivityRecord r = stack.mPausingActivity;
+ if (r != null && !r.isState(PAUSED, STOPPED, STOPPING, FINISHING)) {
+ if (DEBUG_STATES) {
+ Slog.d(TAG_STATES, "allPausedActivitiesComplete: r=" + r
+ + " state=" + r.getState());
+ pausing[0] = false;
+ } else {
+ return true;
+ }
}
}
- }
- }
+ return false;
+ });
+ if (hasActivityNotCompleted) {
+ return false;
}
- return pausing;
+ return pausing[0];
}
/**
@@ -3375,8 +3334,6 @@
* <li>The top activity explicitly belongs to {@param userId}.</li>
* <li>The top activity returns a result to an activity belonging to {@param userId}.</li>
* </ul>
- *
- * @return {@code true} if the top activity looks like it belongs to {@param userId}.
*/
private void taskTopActivityIsUser(Task task, @UserIdInt int userId) {
// To handle the case that work app is in the task but just is not the top one.
@@ -3394,15 +3351,14 @@
}
void cancelInitializingActivities() {
- for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- taskDisplayArea.getStackAt(sNdx).cancelInitializingActivities();
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ // We don't want to clear starting window for activities that aren't occluded
+ // as we need to display their starting window until they are done initializing.
+ taskDisplayArea.getStackAt(sNdx).forAllOccludedActivities(
+ ActivityRecord::cancelInitializing);
}
- }
+ });
}
Task anyTaskForId(int id) {
@@ -3505,24 +3461,20 @@
} else {
// Set power mode when the activity's process is different than the current top resumed
// activity on all display areas, or if there are no resumed activities in the system.
- boolean noResumedActivities = true;
- boolean allFocusedProcessesDiffer = true;
- for (int displayNdx = 0; displayNdx < getChildCount(); ++displayNdx) {
- final DisplayContent dc = getChildAt(displayNdx);
- for (int tdaNdx = dc.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = dc.getTaskDisplayAreaAt(tdaNdx);
- final ActivityRecord resumedActivity = taskDisplayArea.getFocusedActivity();
- final WindowProcessController resumedActivityProcess =
- resumedActivity == null ? null : resumedActivity.app;
+ boolean[] noResumedActivities = {true};
+ boolean[] allFocusedProcessesDiffer = {true};
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ final ActivityRecord resumedActivity = taskDisplayArea.getFocusedActivity();
+ final WindowProcessController resumedActivityProcess =
+ resumedActivity == null ? null : resumedActivity.app;
- noResumedActivities &= resumedActivityProcess == null;
- if (resumedActivityProcess != null) {
- allFocusedProcessesDiffer &= !resumedActivityProcess.equals(
- targetActivity.app);
- }
+ noResumedActivities[0] &= resumedActivityProcess == null;
+ if (resumedActivityProcess != null) {
+ allFocusedProcessesDiffer[0] &=
+ !resumedActivityProcess.equals(targetActivity.app);
}
- }
- sendPowerModeLaunch = noResumedActivities || allFocusedProcessesDiffer;
+ });
+ sendPowerModeLaunch = noResumedActivities[0] || allFocusedProcessesDiffer[0];
}
if (sendPowerModeLaunch && mService.mPowerManagerInternal != null) {
@@ -3563,19 +3515,14 @@
}
} else {
ArrayList<ActivityRecord> activities = new ArrayList<>();
- int numDisplays = getChildCount();
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
- for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
- final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
- activities.addAll(stack.getDumpActivitiesLocked(name));
- }
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
+ final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
+ if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
+ activities.addAll(stack.getDumpActivitiesLocked(name));
}
}
- }
+ });
return activities;
}
}
@@ -3608,46 +3555,43 @@
boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient,
String dumpPackage) {
- boolean printed = false;
- boolean needSep = false;
+ boolean[] printed = {false};
+ boolean[] needSep = {false};
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
DisplayContent displayContent = getChildAt(displayNdx);
- if (printed) {
+ if (printed[0]) {
pw.println();
}
pw.print("Display #"); pw.print(displayContent.mDisplayId);
pw.println(" (activities from top to bottom):");
- for (int tdaNdx = displayContent.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = displayContent.getTaskDisplayAreaAt(tdaNdx);
+ displayContent.forAllTaskDisplayAreas(taskDisplayArea -> {
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
- if (needSep) {
+ if (needSep[0]) {
pw.println();
}
- needSep = stack.dump(fd, pw, dumpAll, dumpClient, dumpPackage, false);
- printed |= needSep;
+ needSep[0] = stack.dump(fd, pw, dumpAll, dumpClient, dumpPackage, false);
+ printed[0] |= needSep[0];
}
- }
- for (int tdaNdx = displayContent.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = displayContent.getTaskDisplayAreaAt(tdaNdx);
- printed |= printThisActivity(pw, taskDisplayArea.getFocusedActivity(),
- dumpPackage, needSep, " Resumed: ", () -> {
- pw.println(" Resumed activities in task display areas"
- + " (from top to bottom):");
- });
- }
+ });
+ displayContent.forAllTaskDisplayAreas(taskDisplayArea -> {
+ printed[0] |= printThisActivity(pw, taskDisplayArea.getFocusedActivity(),
+ dumpPackage, needSep[0], " Resumed: ", () ->
+ pw.println(" Resumed activities in task display areas"
+ + " (from top to bottom):"));
+ });
}
- printed |= dumpHistoryList(fd, pw, mStackSupervisor.mFinishingActivities, " ",
+ printed[0] |= dumpHistoryList(fd, pw, mStackSupervisor.mFinishingActivities, " ",
"Fin", false, !dumpAll,
false, dumpPackage, true,
- () -> { pw.println(" Activities waiting to finish:"); }, null);
- printed |= dumpHistoryList(fd, pw, mStackSupervisor.mStoppingActivities, " ",
+ () -> pw.println(" Activities waiting to finish:"), null);
+ printed[0] |= dumpHistoryList(fd, pw, mStackSupervisor.mStoppingActivities, " ",
"Stop", false, !dumpAll,
false, dumpPackage, true,
- () -> { pw.println(" Activities waiting to stop:"); }, null);
+ () -> pw.println(" Activities waiting to stop:"), null);
- return printed;
+ return printed[0];
}
private final class SleepTokenImpl extends ActivityTaskManagerInternal.SleepToken {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 83b7543..970520a 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1419,7 +1419,7 @@
void removeChild(WindowContainer r, String reason) {
// A rootable child task that is now being removed from an organized task. Making sure
// the stack references is keep updated.
- if (mTaskOrganizer != null && mCreatedByOrganizer && r.asTask() != null) {
+ if (mCreatedByOrganizer && r.asTask() != null) {
getDisplayArea().removeStackReferenceIfNeeded((ActivityStack) r);
}
if (!mChildren.contains(r)) {
@@ -3175,6 +3175,33 @@
return false;
}
+ /** Returns the top-most activity that occludes the given one, or {@code null} if none. */
+ @Nullable
+ ActivityRecord getOccludingActivityAbove(ActivityRecord activity) {
+ final ActivityRecord top = getActivity(ActivityRecord::occludesParent,
+ true /* traverseTopToBottom */, activity);
+ return top != activity ? top : null;
+ }
+
+ /** Iterates through all occluded activities. */
+ void forAllOccludedActivities(Consumer<ActivityRecord> handleOccludedActivity) {
+ if (!shouldBeVisible(null /* starting */)) {
+ // The stack is invisible so all activities are occluded.
+ forAllActivities(handleOccludedActivity);
+ return;
+ }
+ final ActivityRecord topOccluding = getOccludingActivityAbove(null);
+ if (topOccluding == null) {
+ // No activities are occluded.
+ return;
+ }
+ // Invoke the callback on the activities behind the top occluding activity.
+ forAllActivities(r -> {
+ handleOccludedActivity.accept(r);
+ return false;
+ }, topOccluding, false /* includeBoundary */, true /* traverseTopToBottom */);
+ }
+
@Override
public SurfaceControl.Builder makeAnimationLeash() {
return super.makeAnimationLeash().setMetadata(METADATA_TASK_ID, mTaskId);
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 205a8d2..8bab106 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;
@@ -65,6 +66,9 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
/**
* {@link DisplayArea} that represents a section of a screen that contains app window containers.
@@ -106,6 +110,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 +333,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 +373,34 @@
} else if (mPreferredTopFocusableStack == child) {
mPreferredTopFocusableStack = null;
}
+
+ // Update the top resumed activity because the preferred top focusable task may be changed.
+ mAtmService.mStackSupervisor.updateTopResumedActivityIfNeeded();
+ }
+
+ @Override
+ boolean forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean> callback,
+ boolean traverseTopToBottom) {
+ return callback.apply(this);
+ }
+
+ @Override
+ void forAllTaskDisplayAreas(Consumer<TaskDisplayArea> callback, boolean traverseTopToBottom) {
+ callback.accept(this);
+ }
+
+ @Nullable
+ @Override
+ <R> R reduceOnAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R> accumulator,
+ @Nullable R initValue, boolean traverseTopToBottom) {
+ return accumulator.apply(this, initValue);
+ }
+
+ @Nullable
+ @Override
+ <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback,
+ boolean traverseTopToBottom) {
+ return callback.apply(this);
}
/**
@@ -633,39 +660,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) {
@@ -1827,14 +1887,14 @@
@Override
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
pw.println(prefix + "TaskDisplayArea " + getName());
- super.dump(pw, prefix, dumpAll);
+ final String doublePrefix = prefix + " ";
+ super.dump(pw, doublePrefix, dumpAll);
if (mPreferredTopFocusableStack != null) {
- pw.println(prefix + " mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
+ pw.println(doublePrefix + "mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
}
if (mLastFocusedStack != null) {
- pw.println(prefix + " mLastFocusedStack=" + mLastFocusedStack);
+ pw.println(doublePrefix + "mLastFocusedStack=" + mLastFocusedStack);
}
- final String doublePrefix = prefix + " ";
final String triplePrefix = doublePrefix + " ";
pw.println(doublePrefix + "Application tokens in top down Z order:");
for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 11c20b6..00ddf82 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -741,9 +741,7 @@
private void adjustBoundsToAvoidConflictInDisplay(@NonNull DisplayContent display,
@NonNull Rect inOutBounds) {
final List<Rect> taskBoundsToCheck = new ArrayList<>();
- int numTaskContainers = display.getTaskDisplayAreaCount();
- for (int tdaNdx = 0; tdaNdx < numTaskContainers; tdaNdx++) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+ display.forAllTaskDisplayAreas(taskDisplayArea -> {
int numStacks = taskDisplayArea.getStackCount();
for (int sNdx = 0; sNdx < numStacks; ++sNdx) {
final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
@@ -755,7 +753,7 @@
taskBoundsToCheck.add(stack.getChildAt(j).getBounds());
}
}
- }
+ }, false /* traverseTopToBottom */);
adjustBoundsToAvoidConflict(display.getBounds(), taskBoundsToCheck, inOutBounds);
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index f70bf18..04d134c 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -603,8 +603,7 @@
throw new IllegalArgumentException("Display " + displayId + " doesn't exist");
}
ArrayList<RunningTaskInfo> out = new ArrayList<>();
- for (int tdaNdx = dc.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = dc.getTaskDisplayAreaAt(tdaNdx);
+ dc.forAllTaskDisplayAreas(taskDisplayArea -> {
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
final Task task = taskDisplayArea.getStackAt(sNdx);
if (activityTypes != null
@@ -613,7 +612,7 @@
}
out.add(task.getTaskInfo());
}
- }
+ });
return out;
}
} finally {
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/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 132d00a..c27d0cd 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.os.Process.INVALID_UID;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
@@ -24,6 +25,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.annotation.Nullable;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -44,7 +46,13 @@
WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit,
DisplayContent dc, boolean ownerCanManageAppTokens) {
- super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens);
+ this(service, token, explicit, dc, ownerCanManageAppTokens, null /* options */);
+ }
+
+ WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit,
+ DisplayContent dc, boolean ownerCanManageAppTokens, @Nullable Bundle options) {
+ super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens, INVALID_UID,
+ false /* roundedCornerOverlay */, false /* fromClientToken */, options);
dc.mWallpaperController.addWallpaperToken(this);
setWindowingMode(WINDOWING_MODE_FULLSCREEN);
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 5a73fab..7b92c28 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;
@@ -94,6 +95,7 @@
import java.util.Comparator;
import java.util.LinkedList;
import java.util.Set;
+import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -1730,6 +1732,145 @@
}
/**
+ * For all {@link TaskDisplayArea} at or below this container call the callback.
+ * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it
+ * returns {@code true}.
+ * @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in
+ * terms of z-order, else from bottom-to-top.
+ * @return {@code true} if the search ended before we reached the end of the hierarchy due to
+ * callback returning {@code true}.
+ */
+ boolean forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean> callback,
+ boolean traverseTopToBottom) {
+ int childCount = mChildren.size();
+ int i = traverseTopToBottom ? childCount - 1 : 0;
+ while (i >= 0 && i < childCount) {
+ if (mChildren.get(i).forAllTaskDisplayAreas(callback, traverseTopToBottom)) {
+ return true;
+ }
+ i += traverseTopToBottom ? -1 : 1;
+ }
+ return false;
+ }
+
+ /**
+ * For all {@link TaskDisplayArea} at or below this container call the callback. Traverses from
+ * top to bottom in terms of z-order.
+ * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it
+ * returns {@code true}.
+ * @return {@code true} if the search ended before we reached the end of the hierarchy due to
+ * callback returning {@code true}.
+ */
+ boolean forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean> callback) {
+ return forAllTaskDisplayAreas(callback, true /* traverseTopToBottom */);
+ }
+
+ /**
+ * For all {@link TaskDisplayArea} at or below this container call the callback.
+ * @param callback Applies on each {@link TaskDisplayArea} found.
+ * @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in
+ * terms of z-order, else from bottom-to-top.
+ */
+ void forAllTaskDisplayAreas(Consumer<TaskDisplayArea> callback, boolean traverseTopToBottom) {
+ int childCount = mChildren.size();
+ int i = traverseTopToBottom ? childCount - 1 : 0;
+ while (i >= 0 && i < childCount) {
+ mChildren.get(i).forAllTaskDisplayAreas(callback, traverseTopToBottom);
+ i += traverseTopToBottom ? -1 : 1;
+ }
+ }
+
+ /**
+ * For all {@link TaskDisplayArea} at or below this container call the callback. Traverses from
+ * top to bottom in terms of z-order.
+ * @param callback Applies on each {@link TaskDisplayArea} found.
+ */
+ void forAllTaskDisplayAreas(Consumer<TaskDisplayArea> callback) {
+ forAllTaskDisplayAreas(callback, true /* traverseTopToBottom */);
+ }
+
+ /**
+ * Performs a reduction on all {@link TaskDisplayArea} at or below this container, using the
+ * provided initial value and an accumulation function, and returns the reduced value.
+ * @param accumulator Applies on each {@link TaskDisplayArea} found with the accumulative result
+ * from the previous call.
+ * @param initValue The initial value to pass to the accumulating function with the first
+ * {@link TaskDisplayArea}.
+ * @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in
+ * terms of z-order, else from bottom-to-top.
+ * @return the accumulative result.
+ */
+ @Nullable
+ <R> R reduceOnAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R> accumulator,
+ @Nullable R initValue, boolean traverseTopToBottom) {
+ int childCount = mChildren.size();
+ int i = traverseTopToBottom ? childCount - 1 : 0;
+ R result = initValue;
+ while (i >= 0 && i < childCount) {
+ result = (R) mChildren.get(i)
+ .reduceOnAllTaskDisplayAreas(accumulator, result, traverseTopToBottom);
+ i += traverseTopToBottom ? -1 : 1;
+ }
+ return result;
+ }
+
+ /**
+ * Performs a reduction on all {@link TaskDisplayArea} at or below this container, using the
+ * provided initial value and an accumulation function, and returns the reduced value. Traverses
+ * from top to bottom in terms of z-order.
+ * @param accumulator Applies on each {@link TaskDisplayArea} found with the accumulative result
+ * from the previous call.
+ * @param initValue The initial value to pass to the accumulating function with the first
+ * {@link TaskDisplayArea}.
+ * @return the accumulative result.
+ */
+ @Nullable
+ <R> R reduceOnAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R> accumulator,
+ @Nullable R initValue) {
+ return reduceOnAllTaskDisplayAreas(accumulator, initValue, true /* traverseTopToBottom */);
+ }
+
+ /**
+ * Finds the first non {@code null} return value from calling the callback on all
+ * {@link TaskDisplayArea} at or below this container.
+ * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it
+ * returns non {@code null}.
+ * @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in
+ * terms of z-order, else from bottom-to-top.
+ * @return the first returned object that is not {@code null}. Returns {@code null} if not
+ * found.
+ */
+ @Nullable
+ <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback,
+ boolean traverseTopToBottom) {
+ int childCount = mChildren.size();
+ int i = traverseTopToBottom ? childCount - 1 : 0;
+ while (i >= 0 && i < childCount) {
+ R result = (R) mChildren.get(i)
+ .getItemFromTaskDisplayAreas(callback, traverseTopToBottom);
+ if (result != null) {
+ return result;
+ }
+ i += traverseTopToBottom ? -1 : 1;
+ }
+ return null;
+ }
+
+ /**
+ * Finds the first non {@code null} return value from calling the callback on all
+ * {@link TaskDisplayArea} at or below this container. Traverses from top to bottom in terms of
+ * z-order.
+ * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it
+ * returns non {@code null}.
+ * @return the first returned object that is not {@code null}. Returns {@code null} if not
+ * found.
+ */
+ @Nullable
+ <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback) {
+ return getItemFromTaskDisplayAreas(callback, true /* traverseTopToBottom */);
+ }
+
+ /**
* Returns 1, 0, or -1 depending on if this container is greater than, equal to, or lesser than
* the input container in terms of z-order.
*/
@@ -1940,6 +2081,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 +2582,7 @@
protected void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
doAnimationFinished(type, anim);
mWmService.onAnimationFinished();
+ mNeedsZBoost = false;
}
/**
@@ -2554,6 +2697,9 @@
pw.print(prefix); pw.println("ContainerAnimator:");
mSurfaceAnimator.dump(pw, prefix + " ");
}
+ if (mLastOrientationSource != null) {
+ pw.println(prefix + "mLastOrientationSource=" + mLastOrientationSource);
+ }
}
final void updateSurfacePosition() {
@@ -2650,6 +2796,11 @@
return null;
}
+ /** Cheap way of doing cast and instanceof. */
+ DisplayArea asDisplayArea() {
+ return null;
+ }
+
/**
* @return {@code true} if window container is manage by a
* {@link android.window.WindowOrganizer}
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..85972bf 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;
@@ -2669,10 +2670,11 @@
}
// TODO(window-container): Clean up dead tokens
if (type == TYPE_WALLPAPER) {
- new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens);
+ new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens,
+ options);
} else {
new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens,
- callingUid, false /* roundedCornerOverlay */, fromClientToken);
+ callingUid, false /* roundedCornerOverlay */, fromClientToken, options);
}
}
} finally {
@@ -5839,27 +5841,6 @@
}
}
- @Override
- public void setForceShowSystemBars(boolean show) {
- boolean isAutomotive = mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_AUTOMOTIVE);
- if (!isAutomotive) {
- throw new UnsupportedOperationException("Force showing system bars is only supported"
- + "for Automotive use cases.");
- }
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Caller does not hold permission "
- + android.Manifest.permission.STATUS_BAR);
- }
- synchronized (mGlobalLock) {
- final PooledConsumer c = PooledLambda.obtainConsumer(
- DisplayPolicy::setForceShowSystemBars, PooledLambda.__(), show);
- mRoot.forAllDisplayPolicies(c);
- c.recycle();
- }
- }
-
public void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
!= PackageManager.PERMISSION_GRANTED) {
@@ -7629,24 +7610,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..86bc0a2 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();
@@ -2185,6 +2191,9 @@
if (isInputMethodTarget()) {
dc.computeImeTarget(true /* updateImeTarget */);
}
+ if (dc.mInputMethodControlTarget == this) {
+ dc.updateImeControlTarget();
+ }
final int type = mAttrs.type;
if (WindowManagerService.excludeWindowTypeFromTapOutTask(type)) {
@@ -2416,12 +2425,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 +2441,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 +3927,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/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 86aacf3..2c1bb3e 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -40,10 +40,12 @@
import static com.android.server.wm.WindowTokenProto.WINDOW_CONTAINER;
import android.annotation.CallSuper;
+import android.annotation.Nullable;
import android.app.IWindowToken;
import android.app.servertransaction.FixedRotationAdjustmentsItem;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.Bundle;
import android.os.Debug;
import android.os.IBinder;
import android.os.RemoteException;
@@ -78,6 +80,13 @@
// The type of window this token is for, as per WindowManager.LayoutParams.
final int windowType;
+ /**
+ * Options that will be used to determine which {@link RootDisplayArea} this window should be
+ * attached to.
+ */
+ @Nullable
+ final Bundle mOptions;
+
/** {@code true} if this holds the rounded corner overlay */
final boolean mRoundedCornerOverlay;
@@ -233,9 +242,17 @@
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid,
boolean roundedCornerOverlay, boolean fromClientToken) {
+ this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, ownerUid,
+ roundedCornerOverlay, fromClientToken, null /* options */);
+ }
+
+ WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
+ DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid,
+ boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options) {
super(service);
token = _token;
windowType = type;
+ mOptions = options;
mPersistOnEmpty = persistOnEmpty;
mOwnerCanManageAppTokens = ownerCanManageAppTokens;
mOwnerUid = ownerUid;
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 75ec224..7bd455a 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -194,7 +194,7 @@
public:
NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper);
- inline sp<InputManager> getInputManager() const { return mInputManager; }
+ inline sp<InputManagerInterface> getInputManager() const { return mInputManager; }
void dump(std::string& dump);
@@ -225,7 +225,7 @@
/* --- InputReaderPolicyInterface implementation --- */
virtual void getReaderConfiguration(InputReaderConfiguration* outConfig);
- virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId);
+ virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId);
virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices);
virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier& identifier);
virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier);
@@ -236,29 +236,26 @@
/* --- InputDispatcherPolicyInterface implementation --- */
- virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
- uint32_t policyFlags) override;
- virtual void notifyConfigurationChanged(nsecs_t when);
- virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<IBinder>& token, const std::string& reason) override;
- virtual void notifyInputChannelBroken(const sp<IBinder>& token);
- virtual void notifyFocusChanged(const sp<IBinder>& oldToken,
- const sp<IBinder>& newToken) override;
- virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override;
- virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override;
- virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
- uint32_t& policyFlags) override;
- virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
- uint32_t& policyFlags) override;
- virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
- const KeyEvent* keyEvent,
- uint32_t policyFlags) override;
- virtual bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent,
- uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) override;
- virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) override;
- virtual bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid,
- int32_t injectorUid) override;
- virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override;
+ void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
+ uint32_t policyFlags) override;
+ void notifyConfigurationChanged(nsecs_t when) override;
+ std::chrono::nanoseconds notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<IBinder>& token,
+ const std::string& reason) override;
+ void notifyInputChannelBroken(const sp<IBinder>& token) override;
+ void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) override;
+ bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override;
+ void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override;
+ void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) override;
+ void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
+ uint32_t& policyFlags) override;
+ nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token, const KeyEvent* keyEvent,
+ uint32_t policyFlags) override;
+ bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent,
+ uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) override;
+ void pokeUserActivity(nsecs_t eventTime, int32_t eventType) override;
+ bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid, int32_t injectorUid) override;
+ void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override;
/* --- PointerControllerPolicyInterface implementation --- */
@@ -270,7 +267,7 @@
virtual int32_t getCustomPointerIconId();
private:
- sp<InputManager> mInputManager;
+ sp<InputManagerInterface> mInputManager;
jobject mServiceObj;
sp<Looper> mLooper;
@@ -299,7 +296,7 @@
sp<SpriteController> spriteController;
// Pointer controller singleton, created and destroyed as needed.
- wp<PointerController> pointerController;
+ std::weak_ptr<PointerController> pointerController;
// Input devices to be disabled
std::set<int32_t> disabledInputDevices;
@@ -342,9 +339,9 @@
}
mInteractive = true;
- mInputManager = new InputManager(this, this);
- defaultServiceManager()->addService(String16("inputflinger"),
- mInputManager, false);
+ InputManager* im = new InputManager(this, this);
+ mInputManager = im;
+ defaultServiceManager()->addService(String16("inputflinger"), im);
}
NativeInputManager::~NativeInputManager() {
@@ -544,15 +541,16 @@
} // release lock
}
-sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32_t /* deviceId */) {
+std::shared_ptr<PointerControllerInterface> NativeInputManager::obtainPointerController(
+ int32_t /* deviceId */) {
ATRACE_CALL();
AutoMutex _l(mLock);
- sp<PointerController> controller = mLocked.pointerController.promote();
+ std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
if (controller == nullptr) {
ensureSpriteControllerLocked();
- controller = new PointerController(this, mLooper, mLocked.spriteController);
+ controller = PointerController::create(this, mLooper, mLocked.spriteController);
mLocked.pointerController = controller;
updateInactivityTimeoutLocked();
}
@@ -694,8 +692,9 @@
return handle->getInputApplicationHandleObjLocalRef(env);
}
-nsecs_t NativeInputManager::notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<IBinder>& token, const std::string& reason) {
+std::chrono::nanoseconds NativeInputManager::notifyAnr(
+ const sp<InputApplicationHandle>& inputApplicationHandle, const sp<IBinder>& token,
+ const std::string& reason) {
#if DEBUG_INPUT_DISPATCHER_POLICY
ALOGD("notifyANR");
#endif
@@ -718,7 +717,7 @@
} else {
assert(newTimeout >= 0);
}
- return newTimeout;
+ return std::chrono::nanoseconds(newTimeout);
}
void NativeInputManager::notifyInputChannelBroken(const sp<IBinder>& token) {
@@ -803,15 +802,14 @@
}
void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) {
- sp<PointerController> controller = mLocked.pointerController.promote();
+ std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
if (controller == nullptr) {
return;
}
bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
- controller->setInactivityTimeout(lightsOut
- ? PointerController::INACTIVITY_TIMEOUT_SHORT
- : PointerController::INACTIVITY_TIMEOUT_NORMAL);
+ controller->setInactivityTimeout(lightsOut ? PointerController::InactivityTimeout::SHORT
+ : PointerController::InactivityTimeout::NORMAL);
}
void NativeInputManager::setPointerSpeed(int32_t speed) {
@@ -891,7 +889,7 @@
void NativeInputManager::setPointerIconType(int32_t iconId) {
AutoMutex _l(mLock);
- sp<PointerController> controller = mLocked.pointerController.promote();
+ std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
if (controller != nullptr) {
controller->updatePointerIcon(iconId);
}
@@ -899,7 +897,7 @@
void NativeInputManager::reloadPointerIcons() {
AutoMutex _l(mLock);
- sp<PointerController> controller = mLocked.pointerController.promote();
+ std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
if (controller != nullptr) {
controller->reloadPointerResources();
}
@@ -907,7 +905,7 @@
void NativeInputManager::setCustomPointerIcon(const SpriteIcon& icon) {
AutoMutex _l(mLock);
- sp<PointerController> controller = mLocked.pointerController.promote();
+ std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
if (controller != nullptr) {
controller->setCustomPointerIcon(icon);
}
@@ -1253,7 +1251,7 @@
}
void NativeInputManager::setMotionClassifierEnabled(bool enabled) {
- mInputManager->setMotionClassifierEnabled(enabled);
+ mInputManager->getClassifier()->setMotionClassifierEnabled(enabled);
}
// ----------------------------------------------------------------------------
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 24da81e..a2e310a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -618,12 +618,6 @@
}
}
- // Diagnostic to ensure that the system is in a base healthy state. Done here as a common
- // non-zygote process.
- if (!VMRuntime.hasBootImageSpaces()) {
- Slog.wtf(TAG, "Runtime is not running with a boot image!");
- }
-
// Loop forever.
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
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..f40d316 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
@@ -41,8 +41,6 @@
import android.app.AppOpsManager;
import android.content.Context;
-import android.content.Intent;
-import android.location.LocationManager;
import android.location.util.identity.CallerIdentity;
import android.platform.test.annotations.Presubmit;
@@ -52,7 +50,6 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import java.util.ArrayList;
@@ -68,7 +65,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 +79,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);
}
}
@@ -183,11 +180,6 @@
eq(false), eq("myfeature"), nullable(String.class));
assertThat(mHelper.startHighPowerLocationMonitoring(identity)).isTrue();
- ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
- verify(mContext).sendBroadcast(intentCaptor.capture());
- assertThat(intentCaptor.getValue().getAction()).isEqualTo(
- LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
-
doReturn(MODE_IGNORED).when(
mAppOps).startOpNoThrow(eq(OP_MONITOR_HIGH_POWER_LOCATION), eq(1000),
eq("mypackage"),
@@ -209,11 +201,6 @@
mHelper.stopHighPowerLocationMonitoring(identity);
verify(mAppOps).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, 1000, "mypackage", "myfeature");
-
- ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
- verify(mContext).sendBroadcast(intentCaptor.capture());
- assertThat(intentCaptor.getValue().getAction()).isEqualTo(
- LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
}
@Test
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index ac2ec58..7fc6bbd7 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -59,6 +59,7 @@
libs: [
"android.hardware.power-java",
"android.hardware.tv.cec-V1.0-java",
+ "android.hardware.vibrator-java",
"android.hidl.manager-V1.0-java",
"android.test.mock",
"android.test.base",
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 6915220..90e1cfc 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -81,6 +81,9 @@
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
<uses-permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID"/>
+ <uses-permission android:name="android.permission.VIBRATE"/>
+ <uses-permission android:name="android.permission.ACCESS_VIBRATOR_STATE"/>
+ <uses-permission android:name="android.permission.VIBRATE_ALWAYS_ON"/>
<!-- Uses API introduced in O (26) -->
<uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
new file mode 100644
index 0000000..c692253
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
@@ -0,0 +1,607 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.intThat;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManagerInternal;
+import android.hardware.vibrator.IVibrator;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IVibratorStateListener;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.VibrationAttributes;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
+
+/**
+ * Tests for {@link VibratorService}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:VibratorServiceTest
+ */
+@Presubmit
+public class VibratorServiceTest {
+
+ private static final int UID = Process.ROOT_UID;
+ private static final String PACKAGE_NAME = "package";
+ private static final VibrationAttributes ALARM_ATTRS =
+ new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_ALARM).build();
+ private static final VibrationAttributes HAPTIC_FEEDBACK_ATTRS =
+ new VibrationAttributes.Builder().setUsage(
+ VibrationAttributes.USAGE_TOUCH).build();
+ private static final VibrationAttributes NOTIFICATION_ATTRS =
+ new VibrationAttributes.Builder().setUsage(
+ VibrationAttributes.USAGE_NOTIFICATION).build();
+ private static final VibrationAttributes RINGTONE_ATTRS =
+ new VibrationAttributes.Builder().setUsage(
+ VibrationAttributes.USAGE_RINGTONE).build();
+
+ @Rule public MockitoRule rule = MockitoJUnit.rule();
+
+ @Mock private PackageManagerInternal mPackageManagerInternalMock;
+ @Mock private PowerManagerInternal mPowerManagerInternalMock;
+ @Mock private PowerSaveState mPowerSaveStateMock;
+ @Mock private Vibrator mVibratorMock;
+ @Mock private VibratorService.NativeWrapper mNativeWrapperMock;
+ @Mock private IVibratorStateListener mVibratorStateListenerMock;
+ @Mock private IBinder mVibratorStateListenerBinderMock;
+
+ private TestLooper mTestLooper;
+ private ContextWrapper mContextSpy;
+
+ @Before
+ public void setUp() throws Exception {
+ mTestLooper = new TestLooper();
+ mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
+
+ MockContentResolver contentResolver = new MockContentResolver(mContextSpy);
+ contentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+
+ when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
+ when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibratorMock);
+ when(mVibratorMock.getDefaultHapticFeedbackIntensity())
+ .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM);
+ when(mVibratorMock.getDefaultNotificationVibrationIntensity())
+ .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM);
+ when(mVibratorMock.getDefaultRingVibrationIntensity())
+ .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM);
+ when(mVibratorStateListenerMock.asBinder()).thenReturn(mVibratorStateListenerBinderMock);
+ when(mPackageManagerInternalMock.getSystemUiServiceComponent())
+ .thenReturn(new ComponentName("", ""));
+ when(mPowerManagerInternalMock.getLowPowerState(PowerManager.ServiceType.VIBRATION))
+ .thenReturn(mPowerSaveStateMock);
+
+ addLocalServiceMock(PackageManagerInternal.class, mPackageManagerInternalMock);
+ addLocalServiceMock(PowerManagerInternal.class, mPowerManagerInternalMock);
+ FakeSettingsProvider.clearSettingsProvider();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.removeServiceForTest(PowerManagerInternal.class);
+ FakeSettingsProvider.clearSettingsProvider();
+ }
+
+ private VibratorService createService() {
+ return new VibratorService(mContextSpy,
+ new VibratorService.Injector() {
+ @Override
+ VibratorService.NativeWrapper getNativeWrapper() {
+ return mNativeWrapperMock;
+ }
+
+ @Override
+ Handler createHandler(Looper looper) {
+ return new Handler(mTestLooper.getLooper());
+ }
+
+ @Override
+ void addService(String name, IBinder service) {
+ // ignore
+ }
+ });
+ }
+
+ @Test
+ public void createService_initializesNativeService() {
+ createService();
+ verify(mNativeWrapperMock).vibratorInit();
+ verify(mNativeWrapperMock).vibratorOff();
+ }
+
+ @Test
+ public void hasVibrator_withVibratorHalPresent_returnsTrue() {
+ when(mNativeWrapperMock.vibratorExists()).thenReturn(true);
+ assertTrue(createService().hasVibrator());
+ }
+
+ @Test
+ public void hasVibrator_withNoVibratorHalPresent_returnsFalse() {
+ when(mNativeWrapperMock.vibratorExists()).thenReturn(false);
+ assertFalse(createService().hasVibrator());
+ }
+
+ @Test
+ public void hasAmplitudeControl_withAmplitudeControlSupport_returnsTrue() {
+ when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true);
+ assertTrue(createService().hasAmplitudeControl());
+ }
+
+ @Test
+ public void hasAmplitudeControl_withNoAmplitudeControlSupport_returnsFalse() {
+ when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(false);
+ assertFalse(createService().hasAmplitudeControl());
+ }
+
+ @Test
+ public void areEffectsSupported_withNullResultFromNative_returnsSupportUnknown() {
+ when(mNativeWrapperMock.vibratorGetSupportedEffects()).thenReturn(null);
+ assertArrayEquals(new int[]{Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN},
+ createService().areEffectsSupported(new int[]{VibrationEffect.EFFECT_CLICK}));
+ }
+
+ @Test
+ public void areEffectsSupported_withSomeEffectsSupported_returnsSupportYesAndNoForEffects() {
+ int[] effects = new int[]{VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_TICK};
+
+ when(mNativeWrapperMock.vibratorGetSupportedEffects())
+ .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK});
+ assertArrayEquals(
+ new int[]{Vibrator.VIBRATION_EFFECT_SUPPORT_YES,
+ Vibrator.VIBRATION_EFFECT_SUPPORT_NO},
+ createService().areEffectsSupported(effects));
+ }
+
+ @Test
+ public void arePrimitivesSupported_withoutComposeCapability_returnsAlwaysFalse() {
+ assertArrayEquals(new boolean[]{false, false},
+ createService().arePrimitivesSupported(new int[]{
+ VibrationEffect.Composition.PRIMITIVE_CLICK,
+ VibrationEffect.Composition.PRIMITIVE_TICK
+ }));
+ }
+
+ @Test
+ public void arePrimitivesSupported_withComposeCapability_returnsAlwaysTrue() {
+ mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ assertArrayEquals(new boolean[]{true, true},
+ createService().arePrimitivesSupported(new int[]{
+ VibrationEffect.Composition.PRIMITIVE_CLICK,
+ VibrationEffect.Composition.PRIMITIVE_QUICK_RISE
+ }));
+ }
+
+ @Test
+ public void setAlwaysOnEffect_withCapabilityAndValidEffect_enablesAlwaysOnEffect() {
+ mockVibratorCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL);
+
+ assertTrue(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1,
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS));
+ verify(mNativeWrapperMock).vibratorAlwaysOnEnable(
+ eq(1L), eq((long) VibrationEffect.EFFECT_CLICK),
+ eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG));
+ }
+
+ @Test
+ public void setAlwaysOnEffect_withNonPrebakedEffect_ignoresEffect() {
+ mockVibratorCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL);
+
+ assertFalse(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1,
+ VibrationEffect.createOneShot(100, 255), ALARM_ATTRS));
+ verify(mNativeWrapperMock, never()).vibratorAlwaysOnDisable(anyLong());
+ verify(mNativeWrapperMock, never()).vibratorAlwaysOnEnable(anyLong(), anyLong(), anyLong());
+ }
+
+ @Test
+ public void setAlwaysOnEffect_withNullEffect_disablesAlwaysOnEffect() {
+ mockVibratorCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL);
+
+ assertTrue(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, null, ALARM_ATTRS));
+ verify(mNativeWrapperMock).vibratorAlwaysOnDisable(eq(1L));
+ }
+
+ @Test
+ public void setAlwaysOnEffect_withoutCapability_ignoresEffect() {
+ assertFalse(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1,
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS));
+ verify(mNativeWrapperMock, never()).vibratorAlwaysOnDisable(anyLong());
+ verify(mNativeWrapperMock, never()).vibratorAlwaysOnEnable(anyLong(), anyLong(), anyLong());
+ }
+
+ @Test
+ public void vibrate_withOneShotAndAmplitudeControl_turnsVibratorOnAndSetsAmplitude() {
+ when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true);
+ VibratorService service = createService();
+ Mockito.clearInvocations(mNativeWrapperMock);
+
+ vibrate(service, VibrationEffect.createOneShot(100, 128));
+ assertTrue(service.isVibrating());
+
+ verify(mNativeWrapperMock).vibratorOff();
+ verify(mNativeWrapperMock).vibratorOn(eq(100L));
+ verify(mNativeWrapperMock).vibratorSetAmplitude(eq(128));
+ }
+
+ @Test
+ public void vibrate_withOneShotAndNoAmplitudeControl_turnsVibratorOnAndIgnoresAmplitude() {
+ VibratorService service = createService();
+ Mockito.clearInvocations(mNativeWrapperMock);
+
+ vibrate(service, VibrationEffect.createOneShot(100, 128));
+ assertTrue(service.isVibrating());
+
+ verify(mNativeWrapperMock).vibratorOff();
+ verify(mNativeWrapperMock).vibratorOn(eq(100L));
+ verify(mNativeWrapperMock, never()).vibratorSetAmplitude(anyInt());
+ }
+
+ @Test
+ public void vibrate_withPrebaked_performsEffect() {
+ when(mNativeWrapperMock.vibratorGetSupportedEffects())
+ .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK});
+ VibratorService service = createService();
+ Mockito.clearInvocations(mNativeWrapperMock);
+
+ vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
+
+ verify(mNativeWrapperMock).vibratorOff();
+ verify(mNativeWrapperMock).vibratorPerformEffect(
+ eq((long) VibrationEffect.EFFECT_CLICK),
+ eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG),
+ any(VibratorService.Vibration.class), eq(false));
+ }
+
+ @Test
+ public void vibrate_withComposed_performsEffect() {
+ mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ VibratorService service = createService();
+ Mockito.clearInvocations(mNativeWrapperMock);
+
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10)
+ .compose();
+ vibrate(service, effect);
+
+ ArgumentCaptor<VibrationEffect.Composition.PrimitiveEffect[]> primitivesCaptor =
+ ArgumentCaptor.forClass(VibrationEffect.Composition.PrimitiveEffect[].class);
+
+ verify(mNativeWrapperMock).vibratorOff();
+ verify(mNativeWrapperMock).vibratorPerformComposedEffect(
+ primitivesCaptor.capture(), any(VibratorService.Vibration.class));
+
+ // Check all primitive effect fields are passed down to the HAL.
+ assertEquals(1, primitivesCaptor.getValue().length);
+ VibrationEffect.Composition.PrimitiveEffect primitive = primitivesCaptor.getValue()[0];
+ assertEquals(VibrationEffect.Composition.PRIMITIVE_CLICK, primitive.id);
+ assertEquals(0.5f, primitive.scale, /* delta= */ 1e-2);
+ assertEquals(10, primitive.delay);
+ }
+
+ @Test
+ public void vibrate_withWaveform_controlsVibratorAmplitudeDuringTotalVibrationTime()
+ throws Exception {
+ when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true);
+ VibratorService service = createService();
+ Mockito.clearInvocations(mNativeWrapperMock);
+
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ new long[] { 10, 10, 10 }, new int[] { 100, 200, 50 }, -1);
+ vibrate(service, effect);
+
+ verify(mNativeWrapperMock).vibratorOff();
+
+ Thread.sleep(5);
+ verify(mNativeWrapperMock).vibratorOn(eq(30L));
+ verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100));
+
+ Thread.sleep(10);
+ verify(mNativeWrapperMock).vibratorSetAmplitude(eq(200));
+
+ Thread.sleep(10);
+ verify(mNativeWrapperMock).vibratorSetAmplitude(eq(50));
+ }
+
+ @Test
+ public void vibrate_withCallback_finishesVibrationWhenCallbackTriggered() {
+ mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ VibratorService service = createService();
+ Mockito.clearInvocations(mNativeWrapperMock);
+
+ doAnswer(invocation -> {
+ ((VibratorService.Vibration) invocation.getArgument(1)).onComplete();
+ return null;
+ }).when(mNativeWrapperMock).vibratorPerformComposedEffect(
+ any(), any(VibratorService.Vibration.class));
+
+ // Use vibration with delay so there is time for the callback to be triggered.
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10)
+ .compose();
+ vibrate(service, effect);
+
+ // Vibration canceled once before perform and once by native callback.
+ verify(mNativeWrapperMock, times(2)).vibratorOff();
+ verify(mNativeWrapperMock).vibratorPerformComposedEffect(
+ any(VibrationEffect.Composition.PrimitiveEffect[].class),
+ any(VibratorService.Vibration.class));
+ }
+
+ @Test
+ public void vibrate_whenBinderDies_cancelsVibration() {
+ mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ VibratorService service = createService();
+ Mockito.clearInvocations(mNativeWrapperMock);
+
+ doAnswer(invocation -> {
+ ((VibratorService.Vibration) invocation.getArgument(1)).binderDied();
+ return null;
+ }).when(mNativeWrapperMock).vibratorPerformComposedEffect(
+ any(), any(VibratorService.Vibration.class));
+
+ // Use vibration with delay so there is time for the callback to be triggered.
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10)
+ .compose();
+ vibrate(service, effect);
+
+ // Vibration canceled once before perform and once by native binder death.
+ verify(mNativeWrapperMock, times(2)).vibratorOff();
+ verify(mNativeWrapperMock).vibratorPerformComposedEffect(
+ any(VibrationEffect.Composition.PrimitiveEffect[].class),
+ any(VibratorService.Vibration.class));
+ }
+
+ @Test
+ public void cancelVibrate_withDeviceVibrating_callsVibratorOff() {
+ VibratorService service = createService();
+ vibrate(service, VibrationEffect.createOneShot(100, 128));
+ assertTrue(service.isVibrating());
+ Mockito.clearInvocations(mNativeWrapperMock);
+
+ service.cancelVibrate(service);
+ assertFalse(service.isVibrating());
+ verify(mNativeWrapperMock).vibratorOff();
+ }
+
+ @Test
+ public void cancelVibrate_withDeviceNotVibrating_ignoresCall() {
+ VibratorService service = createService();
+ Mockito.clearInvocations(mNativeWrapperMock);
+
+ service.cancelVibrate(service);
+ assertFalse(service.isVibrating());
+ verify(mNativeWrapperMock, never()).vibratorOff();
+ }
+
+ @Test
+ public void registerVibratorStateListener_callbacksAreTriggered() throws Exception {
+ VibratorService service = createService();
+
+ service.registerVibratorStateListener(mVibratorStateListenerMock);
+ verify(mVibratorStateListenerMock).onVibrating(false);
+
+ vibrate(service, VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE));
+ verify(mVibratorStateListenerMock).onVibrating(true);
+
+ // Run the scheduled callback to finish one-shot vibration.
+ mTestLooper.moveTimeForward(10);
+ mTestLooper.dispatchAll();
+ verify(mVibratorStateListenerMock, times(2)).onVibrating(false);
+ }
+
+ @Test
+ public void unregisterVibratorStateListener_callbackNotTriggeredAfter() throws Exception {
+ VibratorService service = createService();
+
+ service.registerVibratorStateListener(mVibratorStateListenerMock);
+ verify(mVibratorStateListenerMock).onVibrating(false);
+
+ vibrate(service, VibrationEffect.createOneShot(5, VibrationEffect.DEFAULT_AMPLITUDE));
+ verify(mVibratorStateListenerMock).onVibrating(true);
+
+ service.unregisterVibratorStateListener(mVibratorStateListenerMock);
+ Mockito.clearInvocations(mVibratorStateListenerMock);
+
+ vibrate(service, VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE));
+ verifyNoMoreInteractions(mVibratorStateListenerMock);
+ }
+
+ @Test
+ public void scale_withPrebaked_userIntensitySettingAsEffectStrength() {
+ // Alarm vibration is always VIBRATION_INTENSITY_HIGH.
+ setVibrationIntensityUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_MEDIUM);
+ setVibrationIntensityUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_LOW);
+ setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_OFF);
+ VibratorService service = createService();
+ service.systemReady();
+
+ vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK),
+ ALARM_ATTRS);
+ vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK),
+ NOTIFICATION_ATTRS);
+ vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_DOUBLE_CLICK),
+ HAPTIC_FEEDBACK_ATTRS);
+ vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK),
+ RINGTONE_ATTRS);
+
+ verify(mNativeWrapperMock).vibratorPerformEffect(
+ eq((long) VibrationEffect.EFFECT_CLICK),
+ eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), any(), anyBoolean());
+ verify(mNativeWrapperMock).vibratorPerformEffect(
+ eq((long) VibrationEffect.EFFECT_TICK),
+ eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), any(), anyBoolean());
+ verify(mNativeWrapperMock).vibratorPerformEffect(
+ eq((long) VibrationEffect.EFFECT_DOUBLE_CLICK),
+ eq((long) VibrationEffect.EFFECT_STRENGTH_LIGHT), any(), anyBoolean());
+ verify(mNativeWrapperMock, never()).vibratorPerformEffect(
+ eq((long) VibrationEffect.EFFECT_HEAVY_CLICK), anyLong(), any(), anyBoolean());
+ }
+
+ @Test
+ public void scale_withOneShotAndWaveform_usesScaleLevelOnAmplitude() throws Exception {
+ when(mVibratorMock.getDefaultNotificationVibrationIntensity())
+ .thenReturn(Vibrator.VIBRATION_INTENSITY_LOW);
+ setVibrationIntensityUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_HIGH);
+ setVibrationIntensityUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_LOW);
+ setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_OFF);
+
+ when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true);
+ VibratorService service = createService();
+ service.systemReady();
+
+ vibrate(service, VibrationEffect.createOneShot(20, 100), ALARM_ATTRS);
+ vibrate(service, VibrationEffect.createOneShot(20, 100), NOTIFICATION_ATTRS);
+ vibrate(service, VibrationEffect.createOneShot(20, 255), RINGTONE_ATTRS);
+ vibrate(service, VibrationEffect.createWaveform(new long[] { 10 }, new int[] { 100 }, -1),
+ HAPTIC_FEEDBACK_ATTRS);
+
+ // Waveform effect runs on a separate thread.
+ Thread.sleep(5);
+
+ // Alarm vibration is never scaled.
+ verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100));
+ // Notification vibrations will be scaled with SCALE_VERY_HIGH.
+ verify(mNativeWrapperMock).vibratorSetAmplitude(intThat(amplitude -> amplitude > 150));
+ // Haptic feedback vibrations will be scaled with SCALE_LOW.
+ verify(mNativeWrapperMock).vibratorSetAmplitude(
+ intThat(amplitude -> amplitude < 100 && amplitude > 50));
+ // Ringtone vibration is off.
+ verify(mNativeWrapperMock, never()).vibratorSetAmplitude(eq(255));
+ }
+
+ @Test
+ public void scale_withComposed_usesScaleLevelOnPrimitiveScaleValues() {
+ when(mVibratorMock.getDefaultNotificationVibrationIntensity())
+ .thenReturn(Vibrator.VIBRATION_INTENSITY_LOW);
+ setVibrationIntensityUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_HIGH);
+ setVibrationIntensityUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_LOW);
+ setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_OFF);
+
+ mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ VibratorService service = createService();
+ service.systemReady();
+
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
+ .compose();
+ ArgumentCaptor<VibrationEffect.Composition.PrimitiveEffect[]> primitivesCaptor =
+ ArgumentCaptor.forClass(VibrationEffect.Composition.PrimitiveEffect[].class);
+
+ vibrate(service, effect, ALARM_ATTRS);
+ vibrate(service, effect, NOTIFICATION_ATTRS);
+ vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS);
+ vibrate(service, effect, RINGTONE_ATTRS);
+
+ // Ringtone vibration is off, so only the other 3 are propagated to native.
+ verify(mNativeWrapperMock, times(3)).vibratorPerformComposedEffect(
+ primitivesCaptor.capture(), any());
+
+ List<VibrationEffect.Composition.PrimitiveEffect[]> values =
+ primitivesCaptor.getAllValues();
+
+ // Alarm vibration is never scaled.
+ assertEquals(1f, values.get(0)[0].scale, /* delta= */ 1e-2);
+ assertEquals(0.5f, values.get(0)[1].scale, /* delta= */ 1e-2);
+
+ // Notification vibrations will be scaled with SCALE_VERY_HIGH.
+ assertEquals(1f, values.get(1)[0].scale, /* delta= */ 1e-2);
+ assertTrue(0.7 < values.get(1)[1].scale);
+
+ // Haptic feedback vibrations will be scaled with SCALE_LOW.
+ assertTrue(0.5 < values.get(2)[0].scale);
+ assertTrue(0.5 > values.get(2)[1].scale);
+ }
+
+ private void vibrate(VibratorService service, VibrationEffect effect) {
+ vibrate(service, effect, ALARM_ATTRS);
+ }
+
+ private void vibrate(VibratorService service, VibrationEffect effect,
+ VibrationAttributes attributes) {
+ service.vibrate(UID, PACKAGE_NAME, effect, attributes, "some reason", service);
+ }
+
+ private void mockVibratorCapabilities(int capabilities) {
+ when(mNativeWrapperMock.vibratorGetCapabilities()).thenReturn((long) capabilities);
+ }
+
+ private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+ LocalServices.removeServiceForTest(clazz);
+ LocalServices.addService(clazz, mock);
+ }
+
+ private void setVibrationIntensityUserSetting(String settingName, int value) {
+ Settings.System.putIntForUser(
+ mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 0445bff..5327bf7 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -51,6 +51,7 @@
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -271,6 +272,25 @@
}
@Test
+ public void setServiceInfo_ChangePackageNames_updateSuccess() {
+ assertTrue(mServiceConnection.mPackageNames.isEmpty());
+
+ final AccessibilityServiceInfo serviceInfo = new AccessibilityServiceInfo();
+ updateServiceInfo(serviceInfo, 0, 0, A11Y_SERVICE_FLAG,
+ new String[] {PACKAGE_NAME1, PACKAGE_NAME2},
+ 1000);
+
+ mServiceConnection.setServiceInfo(serviceInfo);
+ assertEquals(serviceInfo.packageNames.length, mServiceConnection.mPackageNames.size());
+ assertTrue(mServiceConnection.mPackageNames.containsAll(
+ Arrays.asList(mServiceConnection.getServiceInfo().packageNames)));
+
+ updateServiceInfo(serviceInfo, 0, 0, A11Y_SERVICE_FLAG, null, 1000);
+ mServiceConnection.setServiceInfo(serviceInfo);
+ assertTrue(mServiceConnection.mPackageNames.isEmpty());
+ }
+
+ @Test
public void canReceiveEvents_hasEventType_returnTrue() {
final AccessibilityServiceInfo serviceInfo = new AccessibilityServiceInfo();
updateServiceInfo(serviceInfo,
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/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 09f946d..e7eff00 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -102,13 +102,13 @@
@Before
public void setupDefaultAbiBehavior() throws Exception {
when(mMockPackageAbiHelper.derivePackageAbi(
- any(AndroidPackage.class), anyBoolean(), nullable(String.class), anyBoolean()))
+ any(AndroidPackage.class), anyBoolean(), nullable(String.class)))
.thenReturn(new Pair<>(
new PackageAbiHelper.Abis("derivedPrimary", "derivedSecondary"),
new PackageAbiHelper.NativeLibraryPaths(
"derivedRootDir", true, "derivedNativeDir", "derivedNativeDir2")));
- when(mMockPackageAbiHelper.getNativeLibraryPaths(
- any(AndroidPackage.class), any(PackageSetting.class), any(File.class)))
+ when(mMockPackageAbiHelper.deriveNativeLibraryPaths(
+ any(AndroidPackage.class), anyBoolean(), any(File.class)))
.thenReturn(new PackageAbiHelper.NativeLibraryPaths(
"getRootDir", true, "getNativeDir", "getNativeDir2"
));
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
index 6d9b43a..cd2c9230 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
@@ -252,6 +252,7 @@
verify(mMockDataHelper).destroyAppDataSnapshot(eq(123), pkgRollbackInfoFor(PKG_2), eq(111));
verify(mMockDataHelper).destroyAppDataSnapshot(eq(123), pkgRollbackInfoFor(PKG_2), eq(222));
verify(mMockDataHelper, never()).destroyApexDeSnapshots(anyInt());
+ verify(mMockDataHelper, never()).destroyApexCeSnapshots(anyInt(), anyInt());
assertThat(rollback.isDeleted()).isTrue();
}
@@ -273,6 +274,8 @@
verify(mMockDataHelper, never())
.destroyAppDataSnapshot(anyInt(), pkgRollbackInfoFor(PKG_2), anyInt());
verify(mMockDataHelper).destroyApexDeSnapshots(123);
+ verify(mMockDataHelper).destroyApexCeSnapshots(111, 123);
+ verify(mMockDataHelper).destroyApexCeSnapshots(222, 123);
assertThat(rollback.isDeleted()).isTrue();
}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index f0531f5..9f59763 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -35,6 +35,7 @@
import android.content.pm.PackageManager;
import android.os.HandlerThread;
import android.os.TimestampedValue;
+import android.util.IndentingPrintWriter;
import androidx.test.runner.AndroidJUnit4;
@@ -46,6 +47,7 @@
import org.junit.runner.RunWith;
import java.io.PrintWriter;
+import java.io.StringWriter;
@RunWith(AndroidJUnit4.class)
public class TimeDetectorServiceTest {
@@ -177,7 +179,8 @@
when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
.thenReturn(PackageManager.PERMISSION_GRANTED);
- mTimeDetectorService.dump(null, null, null);
+ PrintWriter pw = new PrintWriter(new StringWriter());
+ mTimeDetectorService.dump(null, pw, null);
verify(mMockContext).checkCallingOrSelfPermission(eq(android.Manifest.permission.DUMP));
mStubbedTimeDetectorStrategy.verifyDumpCalled();
@@ -251,7 +254,7 @@
}
@Override
- public void dump(PrintWriter pw, String[] args) {
+ public void dump(IndentingPrintWriter pw, String[] args) {
mDumpCalled = true;
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/StubbedTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/StubbedTimeZoneDetectorStrategy.java
rename to services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
index 43a6b8f..dcf3190 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/StubbedTimeZoneDetectorStrategy.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
@@ -25,10 +25,9 @@
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
import android.app.timezonedetector.TimeZoneCapabilities;
import android.app.timezonedetector.TimeZoneConfiguration;
+import android.util.IndentingPrintWriter;
-import java.io.PrintWriter;
-
-class StubbedTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
+class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
private StrategyListener mListener;
@@ -110,7 +109,7 @@
}
@Override
- public void dump(PrintWriter pw, String[] args) {
+ public void dump(IndentingPrintWriter pw, String[] args) {
mDumpCalled = true;
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
index 40ea398..0e2c227 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
@@ -37,7 +37,7 @@
private static final List<String> ARBITRARY_ZONE_IDS = Arrays.asList("TestZoneId");
private Context mMockContext;
- private StubbedTimeZoneDetectorStrategy mStubbedTimeZoneDetectorStrategy;
+ private FakeTimeZoneDetectorStrategy mFakeTimeZoneDetectorStrategy;
private TimeZoneDetectorInternalImpl mTimeZoneDetectorInternal;
private HandlerThread mHandlerThread;
@@ -53,10 +53,10 @@
mHandlerThread.start();
mTestHandler = new TestHandler(mHandlerThread.getLooper());
- mStubbedTimeZoneDetectorStrategy = new StubbedTimeZoneDetectorStrategy();
+ mFakeTimeZoneDetectorStrategy = new FakeTimeZoneDetectorStrategy();
mTimeZoneDetectorInternal = new TimeZoneDetectorInternalImpl(
- mMockContext, mTestHandler, mStubbedTimeZoneDetectorStrategy);
+ mMockContext, mTestHandler, mFakeTimeZoneDetectorStrategy);
}
@After
@@ -72,7 +72,7 @@
mTestHandler.assertTotalMessagesEnqueued(1);
mTestHandler.waitForMessagesToBeProcessed();
- mStubbedTimeZoneDetectorStrategy.verifySuggestGeolocationTimeZoneCalled(timeZoneSuggestion);
+ mFakeTimeZoneDetectorStrategy.verifySuggestGeolocationTimeZoneCalled(timeZoneSuggestion);
}
private static GeolocationTimeZoneSuggestion createGeolocationTimeZoneSuggestion() {
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index aa517ac..971d2e2 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -46,13 +46,16 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
@RunWith(AndroidJUnit4.class)
public class TimeZoneDetectorServiceTest {
private static final int ARBITRARY_USER_ID = 9999;
private Context mMockContext;
- private StubbedTimeZoneDetectorStrategy mStubbedTimeZoneDetectorStrategy;
+ private FakeTimeZoneDetectorStrategy mFakeTimeZoneDetectorStrategy;
private TimeZoneDetectorService mTimeZoneDetectorService;
private HandlerThread mHandlerThread;
@@ -68,10 +71,10 @@
mHandlerThread.start();
mTestHandler = new TestHandler(mHandlerThread.getLooper());
- mStubbedTimeZoneDetectorStrategy = new StubbedTimeZoneDetectorStrategy();
+ mFakeTimeZoneDetectorStrategy = new FakeTimeZoneDetectorStrategy();
mTimeZoneDetectorService = new TimeZoneDetectorService(
- mMockContext, mTestHandler, mStubbedTimeZoneDetectorStrategy);
+ mMockContext, mTestHandler, mFakeTimeZoneDetectorStrategy);
}
@After
@@ -100,7 +103,7 @@
doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
TimeZoneCapabilities capabilities = createTimeZoneCapabilities();
- mStubbedTimeZoneDetectorStrategy.initializeCapabilities(capabilities);
+ mFakeTimeZoneDetectorStrategy.initializeCapabilities(capabilities);
assertEquals(capabilities, mTimeZoneDetectorService.getCapabilities());
@@ -130,7 +133,7 @@
TimeZoneConfiguration configuration =
createTimeZoneConfiguration(false /* autoDetectionEnabled */);
- mStubbedTimeZoneDetectorStrategy.initializeConfiguration(configuration);
+ mFakeTimeZoneDetectorStrategy.initializeConfiguration(configuration);
assertEquals(configuration, mTimeZoneDetectorService.getConfiguration());
@@ -161,7 +164,8 @@
TimeZoneConfiguration autoDetectDisabledConfiguration =
createTimeZoneConfiguration(false /* autoDetectionEnabled */);
- mStubbedTimeZoneDetectorStrategy.initializeConfiguration(autoDetectDisabledConfiguration);
+
+ mFakeTimeZoneDetectorStrategy.initializeConfiguration(autoDetectDisabledConfiguration);
IBinder mockListenerBinder = mock(IBinder.class);
ITimeZoneConfigurationListener mockListener = mock(ITimeZoneConfigurationListener.class);
@@ -177,7 +181,7 @@
// Simulate the configuration being changed and verify the mockListener was notified.
TimeZoneConfiguration autoDetectEnabledConfiguration =
createTimeZoneConfiguration(true /* autoDetectionEnabled */);
- mStubbedTimeZoneDetectorStrategy.updateConfiguration(
+ mFakeTimeZoneDetectorStrategy.updateConfiguration(
ARBITRARY_USER_ID, autoDetectEnabledConfiguration);
verify(mockListener).onChange(autoDetectEnabledConfiguration);
@@ -209,7 +213,7 @@
assertEquals(expectedResult,
mTimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion));
- mStubbedTimeZoneDetectorStrategy.verifySuggestManualTimeZoneCalled(timeZoneSuggestion);
+ mFakeTimeZoneDetectorStrategy.verifySuggestManualTimeZoneCalled(timeZoneSuggestion);
verify(mMockContext).enforceCallingOrSelfPermission(
eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
@@ -261,7 +265,7 @@
anyString());
mTestHandler.waitForMessagesToBeProcessed();
- mStubbedTimeZoneDetectorStrategy.verifySuggestTelephonyTimeZoneCalled(timeZoneSuggestion);
+ mFakeTimeZoneDetectorStrategy.verifySuggestTelephonyTimeZoneCalled(timeZoneSuggestion);
}
@Test
@@ -269,25 +273,26 @@
when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
.thenReturn(PackageManager.PERMISSION_GRANTED);
- mTimeZoneDetectorService.dump(null, null, null);
+ PrintWriter pw = new PrintWriter(new StringWriter());
+ mTimeZoneDetectorService.dump(null, pw, null);
verify(mMockContext).checkCallingOrSelfPermission(eq(android.Manifest.permission.DUMP));
- mStubbedTimeZoneDetectorStrategy.verifyDumpCalled();
+ mFakeTimeZoneDetectorStrategy.verifyDumpCalled();
}
@Test
public void testAutoTimeZoneDetectionChanged() throws Exception {
- mTimeZoneDetectorService.handleAutoTimeZoneDetectionChanged();
+ mTimeZoneDetectorService.handleAutoTimeZoneConfigChanged();
mTestHandler.assertTotalMessagesEnqueued(1);
mTestHandler.waitForMessagesToBeProcessed();
- mStubbedTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneConfigChangedCalled();
+ mFakeTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneConfigChangedCalled();
- mStubbedTimeZoneDetectorStrategy.resetCallTracking();
+ mFakeTimeZoneDetectorStrategy.resetCallTracking();
- mTimeZoneDetectorService.handleAutoTimeZoneDetectionChanged();
+ mTimeZoneDetectorService.handleAutoTimeZoneConfigChanged();
mTestHandler.assertTotalMessagesEnqueued(2);
mTestHandler.waitForMessagesToBeProcessed();
- mStubbedTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneConfigChangedCalled();
+ mFakeTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneConfigChangedCalled();
}
private static TimeZoneConfiguration createTimeZoneConfiguration(
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index 00a7caa..6855445 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -48,13 +48,13 @@
import android.app.timezonedetector.TelephonyTimeZoneSuggestion.Quality;
import android.app.timezonedetector.TimeZoneCapabilities;
import android.app.timezonedetector.TimeZoneConfiguration;
+import android.util.IndentingPrintWriter;
import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedTelephonyTimeZoneSuggestion;
import org.junit.Before;
import org.junit.Test;
-import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -119,7 +119,8 @@
@Test
public void testGetCapabilities() {
new Script()
- .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
TimeZoneCapabilities expectedCapabilities = mFakeCallback.getCapabilities(USER_ID);
assertEquals(expectedCapabilities, mTimeZoneDetectorStrategy.getCapabilities(USER_ID));
}
@@ -127,17 +128,19 @@
@Test
public void testGetConfiguration() {
new Script()
- .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
TimeZoneConfiguration expectedConfiguration = mFakeCallback.getConfiguration(USER_ID);
assertTrue(expectedConfiguration.isComplete());
assertEquals(expectedConfiguration, mTimeZoneDetectorStrategy.getConfiguration(USER_ID));
}
@Test
- public void testCapabilitiesTestInfra_owner() {
+ public void testCapabilitiesTestInfra_unrestricted() {
Script script = new Script();
- script.initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+ script.initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
{
// Check the fake test infra is doing what is expected.
TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
@@ -145,7 +148,8 @@
assertEquals(CAPABILITY_NOT_APPLICABLE, capabilities.getSuggestManualTimeZone());
}
- script.initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED);
+ script.initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED);
{
// Check the fake test infra is doing what is expected.
TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
@@ -155,10 +159,11 @@
}
@Test
- public void testCapabilitiesTestInfra_nonOwner() {
+ public void testCapabilitiesTestInfra_restricted() {
Script script = new Script();
- script.initializeUser(USER_ID, UserCase.NON_OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+ script.initializeUser(USER_ID, UserCase.RESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
{
// Check the fake test infra is doing what is expected.
TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
@@ -166,7 +171,7 @@
assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone());
}
- script.initializeUser(USER_ID, UserCase.NON_OWNER,
+ script.initializeUser(USER_ID, UserCase.RESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED);
{
// Check the fake test infra is doing what is expected.
@@ -177,10 +182,10 @@
}
@Test
- public void testCapabilitiesTestInfra_ownerAutoDetectNotSupported() {
+ public void testCapabilitiesTestInfra_autoDetectNotSupported() {
Script script = new Script();
- script.initializeUser(USER_ID, UserCase.OWNER_AUTO_DETECT_NOT_SUPPORTED,
+ script.initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
{
// Check the fake test infra is doing what is expected.
@@ -189,7 +194,7 @@
assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone());
}
- script.initializeUser(USER_ID, UserCase.OWNER_AUTO_DETECT_NOT_SUPPORTED,
+ script.initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED);
{
// Check the fake test infra is doing what is expected.
@@ -200,9 +205,10 @@
}
@Test
- public void testUpdateConfiguration_owner() {
+ public void testUpdateConfiguration_unrestricted() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
// Set the configuration with auto detection enabled.
script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
@@ -225,9 +231,9 @@
}
@Test
- public void testUpdateConfiguration_nonOwner() {
+ public void testUpdateConfiguration_restricted() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.NON_OWNER,
+ .initializeUser(USER_ID, UserCase.RESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
// Try to update the configuration with auto detection disabled.
@@ -244,9 +250,9 @@
}
@Test
- public void testUpdateConfiguration_ownerAutoDetectNotSupported() {
+ public void testUpdateConfiguration_autoDetectNotSupported() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER_AUTO_DETECT_NOT_SUPPORTED,
+ .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
// Try to update the configuration with auto detection disabled.
@@ -269,7 +275,8 @@
TelephonyTimeZoneSuggestion slotIndex2TimeZoneSuggestion =
createEmptySlotIndex2Suggestion();
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
script.simulateTelephonyTimeZoneSuggestion(slotIndex1TimeZoneSuggestion)
@@ -311,7 +318,8 @@
QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGH);
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
// A low quality suggestions will not be taken: The device time zone setting is left
// uninitialized.
@@ -376,7 +384,8 @@
for (TelephonyTestCase testCase : TELEPHONY_TEST_CASES) {
// Start with the device in a known state.
- script.initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
+ script.initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
TelephonyTimeZoneSuggestion suggestion =
@@ -427,7 +436,8 @@
@Test
public void testTelephonySuggestionsSingleSlotId() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
for (TelephonyTestCase testCase : TELEPHONY_TEST_CASES) {
@@ -493,7 +503,8 @@
TELEPHONY_SCORE_NONE);
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
// Initialize the latest suggestions as empty so we don't need to worry about nulls
// below for the first loop.
@@ -579,7 +590,8 @@
@Test
public void testTelephonySuggestionTimeZoneDetectorStrategyDoesNotAssumeCurrentSetting() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED);
TelephonyTestCase testCase = newTelephonyTestCase(
MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGH);
@@ -613,9 +625,10 @@
}
@Test
- public void testManualSuggestion_owner_simulateAutoTimeZoneEnabled() {
+ public void testManualSuggestion_unrestricted_simulateAutoTimeZoneEnabled() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
// Auto time zone detection is enabled so the manual suggestion should be ignored.
@@ -625,9 +638,9 @@
}
@Test
- public void testManualSuggestion_nonOwner_simulateAutoTimeZoneEnabled() {
+ public void testManualSuggestion_restricted_simulateAutoTimeZoneEnabled() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.NON_OWNER,
+ .initializeUser(USER_ID, UserCase.RESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
@@ -638,9 +651,9 @@
}
@Test
- public void testManualSuggestion_ownerAutoDetectNotSupported_simulateAutoTimeZoneEnabled() {
+ public void testManualSuggestion_autoDetectNotSupported_simulateAutoTimeZoneEnabled() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER_AUTO_DETECT_NOT_SUPPORTED,
+ .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_ENABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
@@ -652,9 +665,10 @@
}
@Test
- public void testManualSuggestion_owner_autoTimeZoneDetectionDisabled() {
+ public void testManualSuggestion_unrestricted_autoTimeZoneDetectionDisabled() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER, CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
+ CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
// Auto time zone detection is disabled so the manual suggestion should be used.
@@ -665,13 +679,13 @@
}
@Test
- public void testManualSuggestion_nonOwner_autoTimeZoneDetectionDisabled() {
+ public void testManualSuggestion_restricted_autoTimeZoneDetectionDisabled() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.NON_OWNER,
+ .initializeUser(USER_ID, UserCase.RESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
- // Only owners have the capability.
+ // Restricted users do not have the capability.
ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris");
script.simulateManualTimeZoneSuggestion(
USER_ID, manualSuggestion, false /* expectedResult */)
@@ -679,13 +693,13 @@
}
@Test
- public void testManualSuggestion_ownerAutoDetectNotSupported_autoTimeZoneDetectionDisabled() {
+ public void testManualSuggestion_autoDetectNotSupported_autoTimeZoneDetectionDisabled() {
Script script = new Script()
- .initializeUser(USER_ID, UserCase.OWNER_AUTO_DETECT_NOT_SUPPORTED,
+ .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
- // Only owners have the capability.
+ // Unrestricted users have the capability.
ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris");
script.simulateManualTimeZoneSuggestion(
USER_ID, manualSuggestion, true /* expectedResult */)
@@ -695,22 +709,22 @@
@Test
public void testAddDumpable() {
new Script()
- .initializeUser(USER_ID, UserCase.OWNER,
+ .initializeUser(USER_ID, UserCase.UNRESTRICTED,
CONFIG_AUTO_TIME_ZONE_DETECTION_DISABLED)
.initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
AtomicBoolean dumpCalled = new AtomicBoolean(false);
class FakeDumpable implements Dumpable {
@Override
- public void dump(PrintWriter pw, String[] args) {
+ public void dump(IndentingPrintWriter pw, String[] args) {
dumpCalled.set(true);
}
}
mTimeZoneDetectorStrategy.addDumpable(new FakeDumpable());
- PrintWriter pw = new PrintWriter(new StringWriter());
+ IndentingPrintWriter ipw = new IndentingPrintWriter(new StringWriter());
String[] args = {"ArgOne", "ArgTwo"};
- mTimeZoneDetectorStrategy.dump(pw, args);
+ mTimeZoneDetectorStrategy.dump(ipw, args);
assertTrue(dumpCalled.get());
}
@@ -912,12 +926,15 @@
/** Simulated user test cases. */
enum UserCase {
- /** A catch-all for users that can set time zone config. */
- OWNER,
- /** A catch-all for users that can't set time zone config. */
- NON_OWNER,
- /** Owner, but auto tz detection is not supported on the device. */
- OWNER_AUTO_DETECT_NOT_SUPPORTED,
+ /** A catch-all for users that can set auto time zone config. */
+ UNRESTRICTED,
+ /** A catch-all for users that can't set auto time zone config. */
+ RESTRICTED,
+ /**
+ * Like {@link #UNRESTRICTED}, but auto tz detection is not
+ * supported on the device.
+ */
+ AUTO_DETECT_NOT_SUPPORTED,
}
/**
@@ -927,7 +944,7 @@
private static TimeZoneCapabilities createCapabilities(
int userId, UserCase userRole, TimeZoneConfiguration configuration) {
switch (userRole) {
- case OWNER: {
+ case UNRESTRICTED: {
int suggestManualTimeZoneCapability = configuration.isAutoDetectionEnabled()
? CAPABILITY_NOT_APPLICABLE : CAPABILITY_POSSESSED;
return new TimeZoneCapabilities.Builder(userId)
@@ -935,14 +952,14 @@
.setSuggestManualTimeZone(suggestManualTimeZoneCapability)
.build();
}
- case NON_OWNER: {
+ case RESTRICTED: {
return new TimeZoneCapabilities.Builder(userId)
.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED)
.setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED)
.build();
}
- case OWNER_AUTO_DETECT_NOT_SUPPORTED: {
+ case AUTO_DETECT_NOT_SUPPORTED: {
return new TimeZoneCapabilities.Builder(userId)
.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_SUPPORTED)
.setSuggestManualTimeZone(CAPABILITY_POSSESSED)
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 668f047..c7b45ef 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -127,8 +127,7 @@
mTask = mStack.getBottomMostTask();
mActivity = mTask.getTopNonFinishingActivity();
- doReturn(false).when(mService).isBooting();
- doReturn(true).when(mService).isBooted();
+ setBooted(mService);
}
@Test
@@ -1535,7 +1534,7 @@
* Sets orientation without notifying the parent to simulate that the display has not applied
* the requested orientation yet.
*/
- private static void setRotatedScreenOrientationSilently(ActivityRecord r) {
+ static void setRotatedScreenOrientationSilently(ActivityRecord r) {
final int rotatedOrentation = r.getConfiguration().orientation == ORIENTATION_PORTRAIT
? SCREEN_ORIENTATION_LANDSCAPE
: SCREEN_ORIENTATION_PORTRAIT;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 37882bb..373eed9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -1331,42 +1331,40 @@
}
@Test
- public void testCheckBehindFullscreenActivity() {
+ public void testIterateOccludedActivity() {
final ArrayList<ActivityRecord> occludedActivities = new ArrayList<>();
- final Consumer<ActivityRecord> handleBehindFullscreenActivity = occludedActivities::add;
+ final Consumer<ActivityRecord> handleOccludedActivity = occludedActivities::add;
final ActivityRecord bottomActivity =
new ActivityBuilder(mService).setStack(mStack).setTask(mTask).build();
final ActivityRecord topActivity =
new ActivityBuilder(mService).setStack(mStack).setTask(mTask).build();
- doReturn(true).when(mStack).shouldBeVisible(any());
- assertTrue(mStack.checkBehindFullscreenActivity(bottomActivity,
- null /* handleBehindFullscreenActivity */));
- assertFalse(mStack.checkBehindFullscreenActivity(topActivity,
- null /* handleBehindFullscreenActivity */));
-
// Top activity occludes bottom activity.
- mStack.checkBehindFullscreenActivity(null /* toCheck */, handleBehindFullscreenActivity);
+ doReturn(true).when(mStack).shouldBeVisible(any());
+ assertTrue(topActivity.shouldBeVisible());
+ assertFalse(bottomActivity.shouldBeVisible());
+
+ mStack.forAllOccludedActivities(handleOccludedActivity);
assertThat(occludedActivities).containsExactly(bottomActivity);
+ // Top activity doesn't occlude parent, so the bottom activity is not occluded.
doReturn(false).when(topActivity).occludesParent();
- assertFalse(mStack.checkBehindFullscreenActivity(bottomActivity,
- null /* handleBehindFullscreenActivity */));
- assertFalse(mStack.checkBehindFullscreenActivity(topActivity,
- null /* handleBehindFullscreenActivity */));
+ assertTrue(bottomActivity.shouldBeVisible());
occludedActivities.clear();
- // Top activity doesn't occlude parent, so the bottom activity is not occluded.
- mStack.checkBehindFullscreenActivity(null /* toCheck */, handleBehindFullscreenActivity);
+ mStack.forAllOccludedActivities(handleOccludedActivity);
assertThat(occludedActivities).isEmpty();
+ // A finishing activity should not occlude other activities behind.
final ActivityRecord finishingActivity =
new ActivityBuilder(mService).setStack(mStack).setTask(mTask).build();
finishingActivity.finishing = true;
doCallRealMethod().when(finishingActivity).occludesParent();
- assertFalse(mStack.checkBehindFullscreenActivity(bottomActivity,
- null /* handleBehindFullscreenActivity */));
- assertFalse(mStack.checkBehindFullscreenActivity(topActivity,
- null /* handleBehindFullscreenActivity */));
+ assertTrue(topActivity.shouldBeVisible());
+ assertTrue(bottomActivity.shouldBeVisible());
+
+ occludedActivities.clear();
+ mStack.forAllOccludedActivities(handleOccludedActivity);
+ assertThat(occludedActivities).isEmpty();
}
@Test
@@ -1392,8 +1390,7 @@
}
mSupervisor.endDeferResume();
- doReturn(false).when(mService).isBooting();
- doReturn(true).when(mService).isBooted();
+ setBooted(mService);
// 2 activities are started while keyguard is locked, so they are waiting to be resolved.
assertFalse(unknownAppVisibilityController.allResolved());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 4a19684e..e3b1d63 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -200,7 +200,8 @@
ai.packageName = "com.android.test.package";
final WindowProcessController wpc =
containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
- ? null : new WindowProcessController(service, ai, null, 0, -1, null, listener);
+ ? null
+ : new WindowProcessController(service, ai, null, 0, -1, null, listener);
doReturn(wpc).when(service).getProcessController(any());
final Intent intent = new Intent();
@@ -211,7 +212,7 @@
IVoiceInteractionSession voiceSession =
containsConditions(preconditions, PRECONDITION_SOURCE_VOICE_SESSION)
- ? mock(IVoiceInteractionSession.class) : null;
+ ? mock(IVoiceInteractionSession.class) : null;
// Create source token
final ActivityBuilder builder = new ActivityBuilder(service).setTask(
@@ -489,13 +490,12 @@
}
private void assertNoTasks(DisplayContent display) {
- for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
+ display.forAllTaskDisplayAreas(taskDisplayArea -> {
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
assertFalse(stack.hasChild());
}
- }
+ });
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 64b5eca..f65d6e0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -67,8 +67,7 @@
@Before
public void setUp() throws Exception {
- doReturn(false).when(mService).isBooting();
- doReturn(true).when(mService).isBooted();
+ setBooted(mService);
}
/** Verify that activity is finished correctly upon request. */
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 1fefb0c..9d0cd26 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -591,15 +591,14 @@
mService.mTaskOrganizerController.setLaunchRoot(mDisplayId,
mSecondary.mRemoteToken.toWindowContainerToken());
DisplayContent dc = mService.mRootWindowContainer.getDisplayContent(mDisplayId);
- for (int tdaNdx = dc.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = dc.getTaskDisplayAreaAt(tdaNdx);
+ dc.forAllTaskDisplayAreas(taskDisplayArea -> {
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
if (!WindowConfiguration.isSplitScreenWindowingMode(stack.getWindowingMode())) {
stack.reparent(mSecondary, POSITION_BOTTOM);
}
}
- }
+ });
}
@Override
public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
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..a8fc66d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -16,15 +16,19 @@
package com.android.server.wm;
+import static android.os.Process.INVALID_UID;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED;
+import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
+import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
@@ -33,13 +37,18 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
import static java.util.stream.Collectors.toList;
import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
+import com.google.android.collect.Lists;
+
import org.hamcrest.CustomTypeSafeMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
@@ -69,11 +78,15 @@
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;
private List<TaskDisplayArea> mTaskDisplayAreaList;
+ private RootDisplayArea mGroupRoot1;
+ private RootDisplayArea mGroupRoot2;
+ private TaskDisplayArea mTda1;
+ private TaskDisplayArea mTda2;
@Before
public void setup() {
@@ -85,33 +98,39 @@
FEATURE_DEFAULT_TASK_CONTAINER);
mTaskDisplayAreaList = new ArrayList<>();
mTaskDisplayAreaList.add(mDefaultTaskDisplayArea);
+ mGroupRoot1 = new SurfacelessDisplayAreaRoot(mWms, "group1", FEATURE_VENDOR_FIRST + 1);
+ mGroupRoot2 = new SurfacelessDisplayAreaRoot(mWms, "group2", FEATURE_VENDOR_FIRST + 2);
+ mTda1 = new TaskDisplayArea(mDisplayContent, mWms, "tda1", FEATURE_VENDOR_FIRST + 3);
+ mTda2 = new TaskDisplayArea(mDisplayContent, mWms, "tda2", FEATURE_VENDOR_FIRST + 4);
}
@Test
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 +147,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 +181,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());
@@ -186,6 +208,271 @@
}
}
+ @Test
+ public void testBuilder_singleRoot_validateSettings() {
+ final DisplayAreaPolicyBuilder builder = new DisplayAreaPolicyBuilder();
+
+ // Root must be set.
+ assertThrows(IllegalStateException.class, () -> builder.build(mWms));
+
+ // IME must be set.
+ builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setTaskDisplayAreas(mTaskDisplayAreaList));
+
+ assertThrows(IllegalStateException.class, () -> builder.build(mWms));
+
+ // Default TaskDisplayArea must be set.
+ builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(Lists.newArrayList(
+ new TaskDisplayArea(mDisplayContent, mWms, "testTda",
+ FEATURE_VENDOR_FIRST + 1))));
+
+ assertThrows(IllegalStateException.class, () -> builder.build(mWms));
+
+ // No exception
+ builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(mTaskDisplayAreaList));
+
+ builder.build(mWms);
+ }
+
+ @Test
+ public void testBuilder_displayAreaGroup_validateSettings() {
+ final DisplayAreaPolicyBuilder builder1 = new DisplayAreaPolicyBuilder();
+
+ // IME must be set to one of the roots.
+ builder1.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot));
+ builder1.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot1)
+ .setTaskDisplayAreas(mTaskDisplayAreaList));
+
+ assertThrows(IllegalStateException.class, () -> builder1.build(mWms));
+
+ // Default TaskDisplayArea must be set.
+ final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder();
+ builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot));
+ builder2.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot1)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(Lists.newArrayList(
+ new TaskDisplayArea(mDisplayContent, mWms, "testTda",
+ FEATURE_VENDOR_FIRST + 1))));
+
+ assertThrows(IllegalStateException.class, () -> builder2.build(mWms));
+
+ // Each DisplayAreaGroup must have at least one TaskDisplayArea.
+ final DisplayAreaPolicyBuilder builder3 = new DisplayAreaPolicyBuilder();
+ builder3.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot));
+ builder3.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot1)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(mTaskDisplayAreaList));
+ builder3.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot2));
+
+ assertThrows(IllegalStateException.class, () -> builder3.build(mWms));
+
+ // No exception
+ final DisplayAreaPolicyBuilder builder4 = new DisplayAreaPolicyBuilder();
+ builder4.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot));
+ builder4.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot1)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(mTaskDisplayAreaList));
+ builder4.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot2)
+ .setTaskDisplayAreas(Lists.newArrayList(
+ new TaskDisplayArea(mDisplayContent, mWms, "testTda",
+ FEATURE_VENDOR_FIRST + 1))));
+
+ builder4.build(mWms);
+ }
+
+ @Test
+ public void testBuilder_displayAreaGroup_attachDisplayAreas() {
+ final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
+ .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setTaskDisplayAreas(mTaskDisplayAreaList))
+ .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot1)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda1)))
+ .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot2)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda2)))
+ .build(mWms);
+
+ assertThat(mDefaultTaskDisplayArea.isDescendantOf(mRoot)).isTrue();
+ assertThat(mGroupRoot1.isDescendantOf(mRoot)).isTrue();
+ assertThat(mGroupRoot2.isDescendantOf(mRoot)).isTrue();
+ assertThat(mImeContainer.isDescendantOf(mGroupRoot1)).isTrue();
+ assertThat(mTda1.isDescendantOf(mGroupRoot1)).isTrue();
+ assertThat(mTda2.isDescendantOf(mGroupRoot2)).isTrue();
+ assertThat(isSibling(mDefaultTaskDisplayArea, mGroupRoot1)).isTrue();
+ assertThat(isSibling(mDefaultTaskDisplayArea, mGroupRoot2)).isTrue();
+ }
+
+ @Test
+ public void testBuilder_displayAreaGroup_createFeatureOnGroup() {
+ final Feature feature1 = new Feature.Builder(mWms.mPolicy, "feature1",
+ FEATURE_VENDOR_FIRST + 5)
+ .all()
+ .except(TYPE_STATUS_BAR)
+ .build();
+ final Feature feature2 = new Feature.Builder(mWms.mPolicy, "feature2",
+ FEATURE_VENDOR_FIRST + 6)
+ .upTo(TYPE_STATUS_BAR)
+ .and(TYPE_NAVIGATION_BAR)
+ .build();
+ final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
+ .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setTaskDisplayAreas(mTaskDisplayAreaList))
+ .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot1)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda1))
+ .addFeature(feature1))
+ .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot2)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda2))
+ .addFeature(feature2))
+ .build(mWms);
+
+ List<DisplayArea<? extends WindowContainer>> feature1DAs =
+ policy.getDisplayAreas(feature1.getId());
+ List<DisplayArea<? extends WindowContainer>> feature2DAs =
+ policy.getDisplayAreas(feature2.getId());
+ for (DisplayArea<? extends WindowContainer> da : feature1DAs) {
+ assertThat(da.isDescendantOf(mGroupRoot1)).isTrue();
+ }
+ for (DisplayArea<? extends WindowContainer> da : feature2DAs) {
+ assertThat(da.isDescendantOf(mGroupRoot2)).isTrue();
+ }
+ }
+
+ @Test
+ public void testBuilder_addWindow_selectContainerForWindowFunc_defaultFunc() {
+ final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
+ .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setTaskDisplayAreas(mTaskDisplayAreaList))
+ .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot1)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda1)))
+ .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot2)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda2)))
+ .build(mWms);
+
+ final WindowToken token = new WindowToken(mWms, mock(IBinder.class),
+ TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
+ true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ false /* fromClientToken */, null /* options */);
+ policy.addWindow(token);
+
+ // By default, window are always added to the root.
+ assertThat(token.isDescendantOf(mRoot)).isTrue();
+ assertThat(token.isDescendantOf(mGroupRoot1)).isFalse();
+ assertThat(token.isDescendantOf(mGroupRoot2)).isFalse();
+ }
+
+ @Test
+ public void testBuilder_addWindow_selectContainerForWindowFunc_selectBasedOnType() {
+ final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
+ new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda1));
+ final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
+ new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda2));
+ final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
+ .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setTaskDisplayAreas(mTaskDisplayAreaList))
+ .addDisplayAreaGroupHierarchy(hierarchy1)
+ .addDisplayAreaGroupHierarchy(hierarchy2)
+ .setSelectRootForWindowFunc((token, options) -> {
+ if (token.windowType == TYPE_STATUS_BAR) {
+ return mGroupRoot1;
+ }
+ return mGroupRoot2;
+ })
+ .build(mWms);
+
+ final WindowToken token1 = new WindowToken(mWms, mock(IBinder.class),
+ TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
+ true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ false /* fromClientToken */, null /* options */);
+ final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class),
+ TYPE_WALLPAPER, true /* persistOnEmpty */, mDisplayContent,
+ true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ false /* fromClientToken */, null /* options */);
+ policy.addWindow(token1);
+ policy.addWindow(token2);
+
+ assertThat(token1.isDescendantOf(mGroupRoot1)).isTrue();
+ assertThat(token2.isDescendantOf(mGroupRoot2)).isTrue();
+ }
+
+ @Test
+ public void testBuilder_addWindow_selectContainerForWindowFunc_selectBasedOnOptions() {
+ final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
+ new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setTaskDisplayAreas(mTaskDisplayAreaList);
+ final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
+ new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot1)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda1));
+ final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
+ new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot2)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda2));
+ final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
+ .setRootHierarchy(hierarchy0)
+ .addDisplayAreaGroupHierarchy(hierarchy1)
+ .addDisplayAreaGroupHierarchy(hierarchy2)
+ .setSelectRootForWindowFunc((token, options) -> {
+ if (options == null) {
+ return mRoot;
+ }
+ if (options.getInt("HIERARCHY_ROOT_ID") == mGroupRoot1.mFeatureId) {
+ return mGroupRoot1;
+ }
+ if (options.getInt("HIERARCHY_ROOT_ID") == mGroupRoot2.mFeatureId) {
+ return mGroupRoot2;
+ }
+ return mRoot;
+ })
+ .build(mWms);
+
+ final Bundle options1 = new Bundle();
+ options1.putInt("HIERARCHY_ROOT_ID", mGroupRoot1.mFeatureId);
+ final Bundle options2 = new Bundle();
+ options2.putInt("HIERARCHY_ROOT_ID", mGroupRoot2.mFeatureId);
+ final WindowToken token0 = new WindowToken(mWms, mock(IBinder.class),
+ TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
+ true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ false /* fromClientToken */, null /* options */);
+ final WindowToken token1 = new WindowToken(mWms, mock(IBinder.class),
+ TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
+ true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ false /* fromClientToken */, options1);
+ final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class),
+ TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
+ true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ false /* fromClientToken */, options2);
+
+ policy.addWindow(token0);
+ policy.addWindow(token1);
+ policy.addWindow(token2);
+
+ assertThat(token0.isDescendantOf(mRoot)).isTrue();
+ assertThat(token1.isDescendantOf(mGroupRoot1)).isTrue();
+ assertThat(token2.isDescendantOf(mGroupRoot2)).isTrue();
+ }
+
private static Resources resourcesWithProvider(String provider) {
Resources mock = mock(Resources.class);
when(mock.getString(
@@ -207,7 +494,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<>();
@@ -255,6 +542,10 @@
};
}
+ private boolean isSibling(WindowContainer da1, WindowContainer da2) {
+ return da1.getParent() != null && da1.getParent() == da2.getParent();
+ }
+
private WindowToken tokenOfType(int type) {
WindowToken m = mock(WindowToken.class);
when(m.getWindowLayerFromType()).thenReturn(
@@ -285,10 +576,14 @@
}
}
- private static class SurfacelessDisplayAreaRoot extends DisplayArea.Root {
+ static class SurfacelessDisplayAreaRoot extends RootDisplayArea {
SurfacelessDisplayAreaRoot(WindowManagerService wms) {
- super(wms);
+ this(wms, "SurfacelessDisplayAreaRoot", FEATURE_ROOT);
+ }
+
+ SurfacelessDisplayAreaRoot(WindowManagerService wms, String name, int featureId) {
+ super(wms, name, featureId);
}
@Override
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/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 880c486..c8ed87d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -19,47 +19,74 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
+import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
import static com.android.server.wm.DisplayArea.Type.ANY;
import static com.android.server.wm.DisplayArea.Type.BELOW_TASKS;
import static com.android.server.wm.DisplayArea.Type.checkChild;
import static com.android.server.wm.DisplayArea.Type.checkSiblings;
import static com.android.server.wm.DisplayArea.Type.typeOf;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.testing.Assert.assertThrows;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
-import android.view.SurfaceControl;
+import com.google.android.collect.Lists;
+
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * Tests for the {@link DisplayArea} container.
+ *
+ * Build/Install/Run:
+ * atest WmTests:DisplayAreaTest
+ */
@Presubmit
public class DisplayAreaTest {
@Rule
public SystemServicesTestRule mWmsRule = new SystemServicesTestRule();
+ private WindowManagerService mWms;
+
+ @Before
+ public void setup() {
+ mWms = mWmsRule.getWindowManagerService();
+ }
+
@Test
public void testDisplayArea_positionChanged_throwsIfIncompatibleChild() {
- WindowManagerService wms = mWmsRule.getWindowManagerService();
- DisplayArea<WindowContainer> parent = new DisplayArea<>(wms, BELOW_TASKS, "Parent");
- DisplayArea<WindowContainer> child = new DisplayArea<>(wms, ANY, "Child");
+ DisplayArea<WindowContainer> parent = new DisplayArea<>(mWms, BELOW_TASKS, "Parent");
+ DisplayArea<WindowContainer> child = new DisplayArea<>(mWms, ANY, "Child");
assertThrows(IllegalStateException.class, () -> parent.addChild(child, 0));
}
@Test
public void testType_typeOf() {
- WindowManagerService wms = mWmsRule.getWindowManagerService();
-
- assertEquals(ABOVE_TASKS, typeOf(new DisplayArea<>(wms, ABOVE_TASKS, "test")));
- assertEquals(ANY, typeOf(new DisplayArea<>(wms, ANY, "test")));
- assertEquals(BELOW_TASKS, typeOf(new DisplayArea<>(wms, BELOW_TASKS, "test")));
+ assertEquals(ABOVE_TASKS, typeOf(new DisplayArea<>(mWms, ABOVE_TASKS, "test")));
+ assertEquals(ANY, typeOf(new DisplayArea<>(mWms, ANY, "test")));
+ assertEquals(BELOW_TASKS, typeOf(new DisplayArea<>(mWms, BELOW_TASKS, "test")));
assertEquals(ABOVE_TASKS, typeOf(createWindowToken(TYPE_APPLICATION_OVERLAY)));
assertEquals(ABOVE_TASKS, typeOf(createWindowToken(TYPE_PRESENTATION)));
@@ -97,21 +124,264 @@
assertThrows(IllegalStateException.class, () -> checkChild(BELOW_TASKS, ANY));
}
+ @Test
+ public void testAsDisplayArea() {
+ final WindowContainer windowContainer = new WindowContainer(mWms);
+ final DisplayArea<WindowContainer> displayArea = new DisplayArea<>(mWms, ANY, "DA");
+ final TaskDisplayArea taskDisplayArea = new TaskDisplayArea(null /* displayContent */,
+ mWms, "TDA", FEATURE_DEFAULT_TASK_CONTAINER);
+
+ assertThat(windowContainer.asDisplayArea()).isNull();
+ assertThat(displayArea.asDisplayArea()).isEqualTo(displayArea);
+ assertThat(taskDisplayArea.asDisplayArea()).isEqualTo(taskDisplayArea);
+ }
+
+ @Test
+ public void testForAllTaskDisplayAreas_onlyTraversesDisplayAreaOfTypeAny() {
+ final RootDisplayArea root =
+ new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWms);
+ final Function<TaskDisplayArea, Boolean> callback0 = tda -> false;
+ final Consumer<TaskDisplayArea> callback1 = tda -> { };
+ final BiFunction<TaskDisplayArea, Integer, Integer> callback2 = (tda, result) -> result;
+ final Function<TaskDisplayArea, TaskDisplayArea> callback3 = tda -> null;
+
+ // Don't traverse the child if the current DA has type BELOW_TASKS
+ final DisplayArea<WindowContainer> da1 = new DisplayArea<>(mWms, BELOW_TASKS, "DA1");
+ final DisplayArea<WindowContainer> da2 = new DisplayArea<>(mWms, BELOW_TASKS, "DA2");
+ root.addChild(da1, POSITION_BOTTOM);
+ da1.addChild(da2, POSITION_TOP);
+ spyOn(da2);
+
+ da1.forAllTaskDisplayAreas(callback0);
+ da1.forAllTaskDisplayAreas(callback1);
+ da1.reduceOnAllTaskDisplayAreas(callback2, 0);
+ da1.getItemFromTaskDisplayAreas(callback3);
+
+ verifyZeroInteractions(da2);
+
+ // Traverse the child if the current DA has type ANY
+ final DisplayArea<WindowContainer> da3 = new DisplayArea<>(mWms, ANY, "DA3");
+ final DisplayArea<WindowContainer> da4 = new DisplayArea<>(mWms, ANY, "DA4");
+ root.addChild(da3, POSITION_TOP);
+ da3.addChild(da4, POSITION_TOP);
+ spyOn(da4);
+
+ da3.forAllTaskDisplayAreas(callback0);
+ da3.forAllTaskDisplayAreas(callback1);
+ da3.reduceOnAllTaskDisplayAreas(callback2, 0);
+ da3.getItemFromTaskDisplayAreas(callback3);
+
+ verify(da4).forAllTaskDisplayAreas(callback0, true /* traverseTopToBottom */);
+ verify(da4).forAllTaskDisplayAreas(callback1, true /* traverseTopToBottom */);
+ verify(da4).reduceOnAllTaskDisplayAreas(callback2, 0 /* initValue */,
+ true /* traverseTopToBottom */);
+ verify(da4).getItemFromTaskDisplayAreas(
+ callback3, true /* traverseTopToBottom */);
+
+ // Don't traverse the child if the current DA has type ABOVE_TASKS
+ final DisplayArea<WindowContainer> da5 = new DisplayArea<>(mWms, ABOVE_TASKS, "DA5");
+ final DisplayArea<WindowContainer> da6 = new DisplayArea<>(mWms, ABOVE_TASKS, "DA6");
+ root.addChild(da5, POSITION_TOP);
+ da5.addChild(da6, POSITION_TOP);
+ spyOn(da6);
+
+ da5.forAllTaskDisplayAreas(callback0);
+ da5.forAllTaskDisplayAreas(callback1);
+ da5.reduceOnAllTaskDisplayAreas(callback2, 0);
+ da5.getItemFromTaskDisplayAreas(callback3);
+
+ verifyZeroInteractions(da6);
+ }
+
+ @Test
+ public void testForAllTaskDisplayAreas_appliesOnTaskDisplayAreaInOrder() {
+ final RootDisplayArea root =
+ new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWms);
+ final DisplayArea<DisplayArea> da1 =
+ new DisplayArea<>(mWms, ANY, "DA1");
+ final DisplayArea<DisplayArea> da2 =
+ new DisplayArea<>(mWms, ANY, "DA2");
+ final TaskDisplayArea tda1 = new TaskDisplayArea(null /* displayContent */,
+ mWms, "TDA1", FEATURE_DEFAULT_TASK_CONTAINER);
+ final TaskDisplayArea tda2 = new TaskDisplayArea(null /* displayContent */,
+ mWms, "TDA2", FEATURE_VENDOR_FIRST);
+ final TaskDisplayArea tda3 = new TaskDisplayArea(null /* displayContent */,
+ mWms, "TDA3", FEATURE_VENDOR_FIRST + 1);
+ root.addChild(da1, POSITION_TOP);
+ root.addChild(da2, POSITION_TOP);
+ da1.addChild(tda1, POSITION_TOP);
+ da2.addChild(tda2, POSITION_TOP);
+ da2.addChild(tda3, POSITION_TOP);
+
+ /* The hierarchy looks like this
+ Root
+ - DA1
+ - TDA1 ------ bottom
+ - DA2
+ - TDA2
+ - TDA3 ------ top
+ */
+
+ // Test forAllTaskDisplayAreas(Consumer<TaskDisplayArea>)
+ List<TaskDisplayArea> actualOrder = new ArrayList<>();
+ root.forAllTaskDisplayAreas(tda -> {
+ actualOrder.add(tda);
+ });
+
+ assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda3, tda2, tda1));
+
+ // Test forAllTaskDisplayAreas(Consumer<TaskDisplayArea>, boolean)
+ actualOrder.clear();
+ root.forAllTaskDisplayAreas(tda -> {
+ actualOrder.add(tda);
+ }, false /* traverseTopToBottom */);
+
+ assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda1, tda2, tda3));
+
+ // Test forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean>)
+ actualOrder.clear();
+ root.forAllTaskDisplayAreas(tda -> {
+ actualOrder.add(tda);
+ return false;
+ });
+
+ assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda3, tda2, tda1));
+
+ // Test forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean>, boolean)
+ actualOrder.clear();
+ root.forAllTaskDisplayAreas(tda -> {
+ actualOrder.add(tda);
+ return false;
+ }, false /* traverseTopToBottom */);
+
+ assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda1, tda2, tda3));
+
+ // Test forAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R>, R)
+ actualOrder.clear();
+ root.reduceOnAllTaskDisplayAreas((tda, result) -> {
+ actualOrder.add(tda);
+ return result;
+ }, 0 /* initValue */);
+
+ assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda3, tda2, tda1));
+
+ // Test forAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R>, R, boolean)
+ actualOrder.clear();
+ root.reduceOnAllTaskDisplayAreas((tda, result) -> {
+ actualOrder.add(tda);
+ return result;
+ }, 0 /* initValue */, false /* traverseTopToBottom */);
+
+ assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda1, tda2, tda3));
+
+ // Test <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback)
+ actualOrder.clear();
+ root.getItemFromTaskDisplayAreas(tda -> {
+ actualOrder.add(tda);
+ return null;
+ });
+
+ assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda3, tda2, tda1));
+
+ // Test <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback, boolean)
+ actualOrder.clear();
+ root.getItemFromTaskDisplayAreas(tda -> {
+ actualOrder.add(tda);
+ return null;
+ }, false /* traverseTopToBottom */);
+
+ assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda1, tda2, tda3));
+ }
+
+ @Test
+ public void testForAllTaskDisplayAreas_returnsWhenCallbackReturnTrue() {
+ final RootDisplayArea root =
+ new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWms);
+ final TaskDisplayArea tda1 = new TaskDisplayArea(null /* displayContent */,
+ mWms, "TDA1", FEATURE_DEFAULT_TASK_CONTAINER);
+ final TaskDisplayArea tda2 = new TaskDisplayArea(null /* displayContent */,
+ mWms, "TDA2", FEATURE_VENDOR_FIRST);
+ root.addChild(tda1, POSITION_TOP);
+ root.addChild(tda2, POSITION_TOP);
+
+ /* The hierarchy looks like this
+ Root
+ - TDA1 ------ bottom
+ - TDA2 ------ top
+ */
+
+ root.forAllTaskDisplayAreas(tda -> {
+ assertThat(tda).isEqualTo(tda2);
+ return true;
+ });
+
+ root.forAllTaskDisplayAreas(tda -> {
+ assertThat(tda).isEqualTo(tda1);
+ return true;
+ }, false /* traverseTopToBottom */);
+ }
+
+ @Test
+ public void testReduceOnAllTaskDisplayAreas_returnsTheAccumulativeResult() {
+ final RootDisplayArea root =
+ new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWms);
+ final TaskDisplayArea tda1 = new TaskDisplayArea(null /* displayContent */,
+ mWms, "TDA1", FEATURE_DEFAULT_TASK_CONTAINER);
+ final TaskDisplayArea tda2 = new TaskDisplayArea(null /* displayContent */,
+ mWms, "TDA2", FEATURE_VENDOR_FIRST);
+ root.addChild(tda1, POSITION_TOP);
+ root.addChild(tda2, POSITION_TOP);
+
+ /* The hierarchy looks like this
+ Root
+ - TDA1 ------ bottom
+ - TDA2 ------ top
+ */
+
+ String accumulativeName = root.reduceOnAllTaskDisplayAreas((tda, result) ->
+ result + tda.getName(), "" /* initValue */);
+ assertThat(accumulativeName).isEqualTo("TDA2TDA1");
+
+ accumulativeName = root.reduceOnAllTaskDisplayAreas((tda, result) ->
+ result + tda.getName(), "" /* initValue */, false /* traverseTopToBottom */);
+ assertThat(accumulativeName).isEqualTo("TDA1TDA2");
+ }
+
+ @Test
+ public void testGetItemFromTaskDisplayAreas_returnsWhenCallbackReturnNotNull() {
+ final RootDisplayArea root =
+ new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWms);
+ final TaskDisplayArea tda1 = new TaskDisplayArea(null /* displayContent */,
+ mWms, "TDA1", FEATURE_DEFAULT_TASK_CONTAINER);
+ final TaskDisplayArea tda2 = new TaskDisplayArea(null /* displayContent */,
+ mWms, "TDA2", FEATURE_VENDOR_FIRST);
+ root.addChild(tda1, POSITION_TOP);
+ root.addChild(tda2, POSITION_TOP);
+
+ /* The hierarchy looks like this
+ Root
+ - TDA1 ------ bottom
+ - TDA2 ------ top
+ */
+
+ TaskDisplayArea result = root.getItemFromTaskDisplayAreas(tda -> {
+ assertThat(tda).isEqualTo(tda2);
+ return tda;
+ });
+
+ assertThat(result).isEqualTo(tda2);
+
+ result = root.getItemFromTaskDisplayAreas(tda -> {
+ assertThat(tda).isEqualTo(tda1);
+ return tda;
+ }, false /* traverseTopToBottom */);
+
+ assertThat(result).isEqualTo(tda1);
+ }
+
private WindowToken createWindowToken(int type) {
return new WindowToken(mWmsRule.getWindowManagerService(), new Binder(),
type, false /* persist */, null /* displayContent */,
false /* canManageTokens */);
}
-
- private static class SurfacelessDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
-
- SurfacelessDisplayArea(WindowManagerService wms, Type type, String name) {
- super(wms, type, name);
- }
-
- @Override
- SurfaceControl.Builder makeChildSurface(WindowContainer child) {
- return new MockSurfaceControlBuilder();
- }
- }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 4466a66..018b302 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -96,13 +96,10 @@
import android.util.DisplayMetrics;
import android.view.DisplayCutout;
import android.view.Gravity;
-import android.view.IDisplayWindowInsetsController;
import android.view.IDisplayWindowRotationCallback;
import android.view.IDisplayWindowRotationController;
import android.view.ISystemGestureExclusionListener;
import android.view.IWindowManager;
-import android.view.InsetsSourceControl;
-import android.view.InsetsState;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceControl.Transaction;
@@ -482,12 +479,17 @@
final DisplayContent defaultDisplay = mWm.getDefaultDisplayContentLocked();
final WindowState[] windows = createNotDrawnWindowsOn(defaultDisplay,
TYPE_WALLPAPER, TYPE_APPLICATION);
+ final WindowState wallpaper = windows[0];
+ assertTrue(wallpaper.mIsWallpaper);
+ // By default WindowState#mWallpaperVisible is false.
+ assertFalse(wallpaper.isVisible());
// Verify waiting for windows to be drawn.
assertTrue(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot());
- // Verify not waiting for drawn windows.
- makeWindowsDrawnState(windows, WindowStateAnimator.HAS_DRAWN);
+ // Verify not waiting for drawn window and invisible wallpaper.
+ setDrawnState(WindowStateAnimator.READY_TO_SHOW, wallpaper);
+ setDrawnState(WindowStateAnimator.HAS_DRAWN, windows[1]);
assertFalse(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot());
}
@@ -508,26 +510,10 @@
assertTrue(secondaryDisplay.shouldWaitForSystemDecorWindowsOnBoot());
// Verify not waiting for drawn windows on display with system decorations.
- makeWindowsDrawnState(windows, WindowStateAnimator.HAS_DRAWN);
+ setDrawnState(WindowStateAnimator.HAS_DRAWN, windows);
assertFalse(secondaryDisplay.shouldWaitForSystemDecorWindowsOnBoot());
}
- @Test
- public void testShouldWaitForSystemDecorWindowsOnBoot_OnWindowReadyToShowAndDrawn() {
- mWm.mSystemBooted = true;
- final DisplayContent defaultDisplay = mWm.getDefaultDisplayContentLocked();
- final WindowState[] windows = createNotDrawnWindowsOn(defaultDisplay,
- TYPE_WALLPAPER, TYPE_APPLICATION);
-
- // Verify waiting for windows to be drawn.
- makeWindowsDrawnState(windows, WindowStateAnimator.READY_TO_SHOW);
- assertTrue(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot());
-
- // Verify not waiting for drawn windows.
- makeWindowsDrawnState(windows, WindowStateAnimator.HAS_DRAWN);
- assertFalse(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot());
- }
-
private WindowState[] createNotDrawnWindowsOn(DisplayContent displayContent, int... types) {
final WindowState[] windows = new WindowState[types.length];
for (int i = 0; i < types.length; i++) {
@@ -538,9 +524,9 @@
return windows;
}
- private static void makeWindowsDrawnState(WindowState[] windows, int state) {
+ private static void setDrawnState(int state, WindowState... windows) {
for (WindowState window : windows) {
- window.mHasSurface = true;
+ window.mHasSurface = state != WindowStateAnimator.NO_SURFACE;
window.mWinAnimator.mDrawState = state;
}
}
@@ -961,6 +947,26 @@
}
@Test
+ public void testComputeImeControlTarget_exitingApp() throws Exception {
+ final DisplayContent dc = createNewDisplay();
+
+ WindowState exitingWin = createWindow(null, TYPE_BASE_APPLICATION, "exiting app");
+ makeWindowVisible(exitingWin);
+ exitingWin.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
+ exitingWin.mAnimatingExit = true;
+
+ dc.mInputMethodControlTarget = exitingWin;
+ dc.mInputMethodTarget = dc.mInputMethodInputTarget =
+ createWindow(null, TYPE_BASE_APPLICATION, "starting app");
+
+ assertEquals(exitingWin, dc.computeImeControlTarget());
+
+ exitingWin.removeImmediately();
+
+ assertEquals(dc.mInputMethodInputTarget, dc.computeImeControlTarget());
+ }
+
+ @Test
public void testComputeImeControlTarget_splitscreen() throws Exception {
final DisplayContent dc = createNewDisplay();
dc.mInputMethodInputTarget = createWindow(null, TYPE_BASE_APPLICATION, "app");
@@ -981,28 +987,6 @@
assertEquals(mAppWindow, mDisplayContent.computeImeControlTarget());
}
- private IDisplayWindowInsetsController createDisplayWindowInsetsController() {
- return new IDisplayWindowInsetsController.Stub() {
-
- @Override
- public void insetsChanged(InsetsState insetsState) throws RemoteException {
- }
-
- @Override
- public void insetsControlChanged(InsetsState insetsState,
- InsetsSourceControl[] insetsSourceControls) throws RemoteException {
- }
-
- @Override
- public void showInsets(int i, boolean b) throws RemoteException {
- }
-
- @Override
- public void hideInsets(int i, boolean b) throws RemoteException {
- }
- };
- }
-
@Test
public void testUpdateSystemGestureExclusion() throws Exception {
final DisplayContent dc = createNewDisplay();
@@ -1291,8 +1275,7 @@
@Test
public void testNoFixedRotationWithPip() {
// Make resume-top really update the activity state.
- doReturn(false).when(mWm.mAtmService).isBooting();
- doReturn(true).when(mWm.mAtmService).isBooted();
+ setBooted(mWm.mAtmService);
// Speed up the test by a few seconds.
mWm.mAtmService.deferWindowLayout();
doNothing().when(mWm).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
@@ -1499,7 +1482,6 @@
@Test
public void testEnsureActivitiesVisibleNotRecursive() {
final TaskDisplayArea mockTda = mock(TaskDisplayArea.class);
- doReturn(mockTda).when(mDisplayContent).getTaskDisplayAreaAt(anyInt());
final boolean[] called = { false };
doAnswer(invocation -> {
// The assertion will fail if DisplayArea#ensureActivitiesVisible is called twice.
@@ -1512,6 +1494,24 @@
mDisplayContent.ensureActivitiesVisible(null, 0, false, false);
}
+ @Test
+ public void testSetWindowingModeAtomicallyUpdatesWindoingModeAndDisplayWindowingMode() {
+ final DisplayContent dc = createNewDisplay();
+ final ActivityStack stack =
+ new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootWindowContainer)
+ .setDisplay(dc)
+ .build();
+ doAnswer(invocation -> {
+ Object[] args = invocation.getArguments();
+ final Configuration config = ((Configuration) args[0]);
+ assertEquals(config.windowConfiguration.getWindowingMode(),
+ config.windowConfiguration.getDisplayWindowingMode());
+ return null;
+ }).when(stack).onConfigurationChanged(any());
+ dc.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ dc.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ }
+
private boolean isOptionsPanelAtRight(int displayId) {
return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index b69a1f3..da7c41a7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -828,28 +828,6 @@
}
@Test
- public void forceShowSystemBars_clearsSystemUIFlags() {
- doCallRealMethod().when(mDisplayPolicy).updateSystemUiVisibilityLw();
- mDisplayPolicy.mLastSystemUiFlags |= SYSTEM_UI_FLAG_FULLSCREEN;
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- mWindow.mAttrs.flags =
- FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- mWindow.mSystemUiVisibility = SYSTEM_UI_FLAG_FULLSCREEN;
- mDisplayPolicy.setForceShowSystemBars(true);
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- // triggers updateSystemUiVisibilityLw which will reset the flags as needed
- int finishPostLayoutPolicyLw = mDisplayPolicy.focusChangedLw(mWindow, mWindow);
-
- assertEquals(WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT, finishPostLayoutPolicyLw);
- assertEquals(0, mDisplayPolicy.mLastSystemUiFlags);
- assertEquals(0, mWindow.mAttrs.systemUiVisibility);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- }
-
- @Test
public void testScreenDecorWindows() {
final WindowState decorWindow = spy(
createWindow(null, TYPE_APPLICATION_OVERLAY, "decorWindow"));
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index c794e1a..87bc7f1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -172,8 +172,9 @@
}
@Test
- public void testControlsForDispatch_forceShowSystemBarsFromExternal_appHasNoControl() {
- mDisplayContent.getDisplayPolicy().setForceShowSystemBars(true);
+ public void testControlsForDispatch_remoteInsetsControllerControlsBars_appHasNoControl() {
+ mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController());
+ mDisplayContent.getInsetsPolicy().setRemoteInsetsControllerControlsSystemBars(true);
addWindow(TYPE_STATUS_BAR, "statusBar");
addWindow(TYPE_NAVIGATION_BAR, "navBar");
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index add2054..13f04d2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -46,7 +46,6 @@
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.server.testutils.OffsettableClock;
@@ -244,7 +243,7 @@
}
@Test
- public void testChange() throws Exception {
+ public void testChangeToSmallerSize() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
mDisplayContent.mChangingContainers.add(win.mActivityRecord);
try {
@@ -279,8 +278,61 @@
assertEquals(false, app.isTranslucent);
verify(mMockTransaction).setPosition(
mMockLeash, app.startBounds.left, app.startBounds.top);
- verify(mMockTransaction).setWindowCrop(mMockLeash, 200, 200);
+ verify(mMockTransaction).setWindowCrop(
+ mMockLeash, app.startBounds.width(), app.startBounds.height());
verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0);
+ verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, -1, -1);
+
+ finishedCaptor.getValue().onAnimationFinished();
+ verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_WINDOW_ANIMATION),
+ eq(record.mAdapter));
+ verify(mThumbnailFinishedCallback).onAnimationFinished(
+ eq(ANIMATION_TYPE_WINDOW_ANIMATION), eq(record.mThumbnailAdapter));
+ } finally {
+ mDisplayContent.mChangingContainers.clear();
+ }
+ }
+
+ @Test
+ public void testChangeTolargerSize() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mChangingContainers.add(win.mActivityRecord);
+ try {
+ final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
+ win.mActivityRecord, new Point(0, 0), null, new Rect(0, 0, 200, 200),
+ new Rect(50, 100, 150, 150));
+ assertNotNull(record.mThumbnailAdapter);
+ ((AnimationAdapter) record.mAdapter)
+ .startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION,
+ mFinishedCallback);
+ ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
+ mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
+ mController.goodToGo();
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ finishedCaptor.capture());
+ assertEquals(1, appsCaptor.getValue().length);
+ final RemoteAnimationTarget app = appsCaptor.getValue()[0];
+ assertEquals(RemoteAnimationTarget.MODE_CHANGING, app.mode);
+ assertEquals(new Point(0, 0), app.position);
+ assertEquals(new Rect(0, 0, 200, 200), app.sourceContainerBounds);
+ assertEquals(new Rect(50, 100, 150, 150), app.startBounds);
+ assertEquals(mMockLeash, app.leash);
+ assertEquals(mMockThumbnailLeash, app.startLeash);
+ assertEquals(win.mWinAnimator.mLastClipRect, app.clipRect);
+ assertEquals(false, app.isTranslucent);
+ verify(mMockTransaction).setPosition(
+ mMockLeash, app.startBounds.left, app.startBounds.top);
+ verify(mMockTransaction).setWindowCrop(
+ mMockLeash, app.startBounds.width(), app.startBounds.height());
+ verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0);
+ verify(mMockTransaction).setWindowCrop(mMockThumbnailLeash, -1, -1);
finishedCaptor.getValue().onAnimationFinished();
verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_WINDOW_ANIMATION),
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 5dba0045..d9ec0b1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -42,6 +42,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -57,8 +58,10 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.platform.test.annotations.Presubmit;
+import android.util.MergedConfiguration;
import android.util.Pair;
import androidx.test.filters.MediumTest;
@@ -221,6 +224,35 @@
null /* target */, null /* targetOptions */);
}
+ @Test
+ public void testAwakeFromSleepingWithAppConfiguration() {
+ final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
+ final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true).build();
+ activity.moveFocusableActivityToTop("test");
+ assertTrue(activity.getStack().isFocusedStackOnDisplay());
+ ActivityRecordTests.setRotatedScreenOrientationSilently(activity);
+
+ final Configuration rotatedConfig = new Configuration();
+ display.computeScreenConfiguration(rotatedConfig, display.getDisplayRotation()
+ .rotationForOrientation(activity.getOrientation(), display.getRotation()));
+ assertNotEquals(activity.getConfiguration().orientation, rotatedConfig.orientation);
+ // Assume the activity was shown in different orientation. For example, the top activity is
+ // landscape and the portrait lockscreen is shown.
+ activity.setLastReportedConfiguration(
+ new MergedConfiguration(mService.getGlobalConfiguration(), rotatedConfig));
+ activity.setState(ActivityState.STOPPED, "sleep");
+
+ display.setIsSleeping(true);
+ doReturn(false).when(display).shouldSleep();
+ // Allow to resume when awaking.
+ setBooted(mService);
+ mRootWindowContainer.applySleepTokens(true);
+
+ // The display orientation should be changed by the activity so there is no relaunch.
+ verify(activity, never()).relaunchActivityLocked(anyBoolean());
+ assertEquals(rotatedConfig.orientation, display.getConfiguration().orientation);
+ }
+
/**
* Verifies that removal of activity with task and stack is done correctly.
*/
@@ -260,13 +292,11 @@
assertEquals(originalStackCount + 1, defaultTaskDisplayArea.getStackCount());
final DisplayContent dc = defaultTaskDisplayArea.getDisplayContent();
- doReturn(2).when(dc).getTaskDisplayAreaCount();
final TaskDisplayArea secondTaskDisplayArea = new TaskDisplayArea(dc,
mRootWindowContainer.mWmService, "SecondaryTaskDisplayArea", FEATURE_VENDOR_FIRST);
// Add second display area right above the default one
defaultTaskDisplayArea.getParent().addChild(secondTaskDisplayArea,
defaultTaskDisplayArea.getParent().mChildren.indexOf(defaultTaskDisplayArea) + 1);
- doReturn(secondTaskDisplayArea).when(dc).getTaskDisplayAreaAt(1);
final ActivityStack secondStack = secondTaskDisplayArea.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
new ActivityBuilder(mService).setCreateTask(true).setStack(secondStack)
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 35d1b17..181de8e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -148,7 +148,7 @@
@Test
public void testAllPausedActivitiesComplete() {
DisplayContent displayContent = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY);
- TaskDisplayArea taskDisplayArea = displayContent.getTaskDisplayAreaAt(0);
+ TaskDisplayArea taskDisplayArea = displayContent.getDefaultTaskDisplayArea();
ActivityStack stack = taskDisplayArea.getStackAt(0);
ActivityRecord activity = createActivityRecord(displayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
index d7462f8..53c2a5b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
import android.os.Handler;
import android.testing.DexmakerShareClassLoaderRule;
@@ -67,6 +69,15 @@
}
/**
+ * Make the system booted, so that {@link ActivityStack#resumeTopActivityInnerLocked} can really
+ * be executed to update activity state and configuration when resuming the current top.
+ */
+ static void setBooted(ActivityTaskManagerService atmService) {
+ doReturn(false).when(atmService).isBooting();
+ doReturn(true).when(atmService).isBooted();
+ }
+
+ /**
* Utility class to compare the output of T#toString. It is convenient to have readable output
* of assertion if the string content can represent the expected states.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index d2a2732..213c1f5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -43,10 +43,9 @@
// hard-code to FULLSCREEN for tests.
setWindowingMode(WINDOWING_MODE_FULLSCREEN);
spyOn(this);
- for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
- spyOn(getTaskDisplayAreaAt(i));
- }
-
+ forAllTaskDisplayAreas(taskDisplayArea -> {
+ spyOn(taskDisplayArea);
+ });
final DisplayRotation displayRotation = getDisplayRotation();
spyOn(displayRotation);
doAnswer(invocation -> {
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..1266c50 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -60,24 +60,6 @@
@Rule
public ExpectedException mExpectedException = ExpectedException.none();
- @Test
- public void testForceShowSystemBarsThrowsExceptionForNonAutomotive() {
- if (!isAutomotive()) {
- mExpectedException.expect(UnsupportedOperationException.class);
-
- mWm.setForceShowSystemBars(true);
- }
- }
-
- @Test
- public void testForceShowSystemBarsDoesNotThrowExceptionForAutomotiveWithStatusBarPermission() {
- if (isAutomotive()) {
- mExpectedException.none();
-
- mWm.setForceShowSystemBars(true);
- }
- }
-
private boolean isAutomotive() {
return getInstrumentation().getTargetContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_AUTOMOTIVE);
@@ -166,8 +148,7 @@
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);
// Current focused window
ActivityStack focusedStack = createTaskStackOnDisplay(
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, display);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 5f40b6d..04d52af 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -673,13 +673,12 @@
private List<Task> getTasksCreatedByOrganizer(DisplayContent dc) {
ArrayList<Task> out = new ArrayList<>();
- for (int tdaNdx = dc.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
- final TaskDisplayArea taskDisplayArea = dc.getTaskDisplayAreaAt(tdaNdx);
+ dc.forAllTaskDisplayAreas(taskDisplayArea -> {
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
final Task t = taskDisplayArea.getStackAt(sNdx);
if (t.mCreatedByOrganizer) out.add(t);
}
- }
+ });
return out;
}
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/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index a1e5b80..156298c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -41,11 +41,15 @@
import android.content.Context;
import android.content.Intent;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.IDisplayWindowInsetsController;
import android.view.IWindow;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
import android.view.SurfaceControl.Transaction;
import android.view.View;
import android.view.WindowManager;
@@ -123,7 +127,7 @@
mChildAppWindowBelow = createCommonWindow(mAppWindow,
TYPE_APPLICATION_MEDIA_OVERLAY,
"mChildAppWindowBelow");
- mDisplayContent.getDisplayPolicy().setForceShowSystemBars(false);
+ mDisplayContent.getInsetsPolicy().setRemoteInsetsControllerControlsSystemBars(false);
// Adding a display will cause freezing the display. Make sure to wait until it's
// unfrozen to not run into race conditions with the tests.
@@ -344,6 +348,32 @@
return createNewDisplay(displayInfo, false /* supportIme */);
}
+ IDisplayWindowInsetsController createDisplayWindowInsetsController() {
+ return new IDisplayWindowInsetsController.Stub() {
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) throws RemoteException {
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] insetsSourceControls) throws RemoteException {
+ }
+
+ @Override
+ public void showInsets(int i, boolean b) throws RemoteException {
+ }
+
+ @Override
+ public void hideInsets(int i, boolean b) throws RemoteException {
+ }
+
+ @Override
+ public void topFocusedWindowChanged(String packageName) {
+ }
+ };
+ }
+
/** Sets the default minimum task size to 1 so that tests can use small task sizes */
void removeGlobalMinSizeRestriction() {
mWm.mAtmService.mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp = 1;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index 23a097e..5264e9a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -20,16 +20,21 @@
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import android.content.res.Configuration;
+import android.os.Bundle;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
@@ -38,6 +43,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.function.BiFunction;
+
/**
* Tests for the {@link WindowToken} class.
*
@@ -205,4 +212,27 @@
false /* fromClientToken */);
assertNotNull(nonClientToken.mSurfaceControl);
}
+
+ @Test
+ public void testWindowAttachedWithOptions() {
+ BiFunction<WindowToken, Bundle, RootDisplayArea> selectFunc =
+ ((DisplayAreaPolicyBuilder.Result) mDisplayContent.mDisplayAreaPolicy)
+ .mSelectRootForWindowFunc;
+ spyOn(selectFunc);
+
+ final WindowToken token1 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class),
+ TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
+ true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */,
+ false /* fromClientToken */, null /* options */);
+
+ verify(selectFunc).apply(token1, null);
+
+ final Bundle options = new Bundle();
+ final WindowToken token2 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class),
+ TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
+ true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */,
+ false /* fromClientToken */, options /* options */);
+
+ verify(selectFunc).apply(token2, options);
+ }
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 7595e3f..6cf0eec 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -164,6 +164,8 @@
private static final int MSG_FUNCTION_SWITCH_TIMEOUT = 17;
private static final int MSG_GADGET_HAL_REGISTERED = 18;
private static final int MSG_RESET_USB_GADGET = 19;
+ private static final int MSG_ACCESSORY_HANDSHAKE_TIMEOUT = 20;
+ private static final int MSG_INCREASE_SENDSTRING_COUNT = 21;
private static final int AUDIO_MODE_SOURCE = 1;
@@ -176,6 +178,13 @@
// Request is cancelled if host does not configure device within 10 seconds.
private static final int ACCESSORY_REQUEST_TIMEOUT = 10 * 1000;
+ /**
+ * Timeout for receiving USB accessory request
+ * Reset when receive next control request
+ * Broadcast intent if not receive next control request or enter next step.
+ */
+ private static final int ACCESSORY_HANDSHAKE_TIMEOUT = 10 * 1000;
+
private static final String BOOT_MODE_PROPERTY = "ro.bootmode";
private static final String ADB_NOTIFICATION_CHANNEL_ID_TV = "usbdevicemanager.adb.tv";
@@ -222,8 +231,19 @@
String accessory = event.get("ACCESSORY");
if (state != null) {
mHandler.updateState(state);
+ } else if ("GETPROTOCOL".equals(accessory)) {
+ if (DEBUG) Slog.d(TAG, "got accessory get protocol");
+ long elapsedRealtime = SystemClock.elapsedRealtime();
+ mHandler.setAccessoryUEventTime(elapsedRealtime);
+ resetAccessoryHandshakeTimeoutHandler();
+ } else if ("SENDSTRING".equals(accessory)) {
+ if (DEBUG) Slog.d(TAG, "got accessory send string");
+ mHandler.sendEmptyMessage(MSG_INCREASE_SENDSTRING_COUNT);
+ resetAccessoryHandshakeTimeoutHandler();
} else if ("START".equals(accessory)) {
if (DEBUG) Slog.d(TAG, "got accessory start");
+ mHandler.removeMessages(MSG_ACCESSORY_HANDSHAKE_TIMEOUT);
+ mHandler.setStartAccessoryTrue();
startAccessoryMode();
}
}
@@ -395,6 +415,23 @@
mHandler.sendEmptyMessage(MSG_UPDATE_USER_RESTRICTIONS);
}
+ /*
+ * Start the timeout-timer upon receiving "get_protocol" uevent.
+ * Restart the timer every time if any succeeding uevnet received.
+ * (Only when USB is under accessory mode)
+ * <p>About the details of the related control request and sequence ordering, refer to
+ * <a href="https://source.android.com/devices/accessories/aoa">AOA</a> developer guide.</p>
+ */
+ private void resetAccessoryHandshakeTimeoutHandler() {
+ long functions = getCurrentFunctions();
+ // some accesories send get_protocol after start accessory
+ if ((functions & UsbManager.FUNCTION_ACCESSORY) == 0) {
+ mHandler.removeMessages(MSG_ACCESSORY_HANDSHAKE_TIMEOUT);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSORY_HANDSHAKE_TIMEOUT),
+ ACCESSORY_HANDSHAKE_TIMEOUT);
+ }
+ }
+
private void startAccessoryMode() {
if (!mHasUsbAccessory) return;
@@ -416,6 +453,8 @@
if (functions != UsbManager.FUNCTION_NONE) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSORY_MODE_ENTER_TIMEOUT),
ACCESSORY_REQUEST_TIMEOUT);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSORY_HANDSHAKE_TIMEOUT),
+ ACCESSORY_HANDSHAKE_TIMEOUT);
setCurrentFunctions(functions);
}
}
@@ -468,6 +507,15 @@
private int mMidiCard;
private int mMidiDevice;
+ /**
+ * mAccessoryConnectionStartTime record the timing of start_accessory
+ * mSendStringCount count the number of receiving uevent send_string
+ * mStartAccessory record whether start_accessory is received
+ */
+ private long mAccessoryConnectionStartTime = 0L;
+ private int mSendStringCount = 0;
+ private boolean mStartAccessory = false;
+
private final Context mContext;
private final UsbAlsaManager mUsbAlsaManager;
private final UsbPermissionManager mPermissionManager;
@@ -645,6 +693,8 @@
// defer accessoryAttached if system is not ready
if (mBootCompleted) {
mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory);
+ removeMessages(MSG_ACCESSORY_HANDSHAKE_TIMEOUT);
+ broadcastUsbAccessoryHandshake();
} // else handle in boot completed
} else {
Slog.e(TAG, "nativeGetAccessoryStrings failed");
@@ -701,6 +751,30 @@
return false;
}
+ private void broadcastUsbAccessoryHandshake() {
+ long elapsedRealtime = SystemClock.elapsedRealtime();
+ long accessoryHandShakeEnd = elapsedRealtime;
+
+ // send a sticky broadcast containing USB accessory handshake information
+ Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_HANDSHAKE)
+ .putExtra(UsbManager.EXTRA_ACCESSORY_UEVENT_TIME,
+ mAccessoryConnectionStartTime)
+ .putExtra(UsbManager.EXTRA_ACCESSORY_STRING_COUNT,
+ mSendStringCount)
+ .putExtra(UsbManager.EXTRA_ACCESSORY_START,
+ mStartAccessory)
+ .putExtra(UsbManager.EXTRA_ACCESSORY_HANDSHAKE_END,
+ accessoryHandShakeEnd);
+
+ if (DEBUG) {
+ Slog.d(TAG, "broadcasting " + intent + " extras: " + intent.getExtras());
+ }
+
+ sendStickyBroadcast(intent);
+ resetUsbAccessoryHandshakeDebuggingInfo();
+ accessoryHandShakeEnd = 0L;
+ }
+
protected void updateUsbStateBroadcastIfNeeded(long functions) {
// send a sticky broadcast containing current USB state
Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
@@ -998,6 +1072,16 @@
}
break;
}
+ case MSG_ACCESSORY_HANDSHAKE_TIMEOUT: {
+ if (DEBUG) {
+ Slog.v(TAG, "Accessory handshake timeout");
+ }
+ broadcastUsbAccessoryHandshake();
+ break;
+ }
+ case MSG_INCREASE_SENDSTRING_COUNT: {
+ mSendStringCount = mSendStringCount + 1;
+ }
}
}
@@ -1015,6 +1099,7 @@
}
if (mCurrentAccessory != null) {
mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory);
+ broadcastUsbAccessoryHandshake();
}
updateUsbNotification(false);
@@ -1282,6 +1367,9 @@
try {
writeStringIfNotNull(dump, "kernel_state", UsbHandlerProto.KERNEL_STATE,
FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim());
+ } catch (FileNotFoundException exNotFound) {
+ Slog.w(TAG, "Ignore missing legacy kernel path in bugreport dump: "
+ + "kernel state:" + STATE_PATH);
} catch (Exception e) {
Slog.e(TAG, "Could not read kernel state", e);
}
@@ -1290,6 +1378,9 @@
writeStringIfNotNull(dump, "kernel_function_list",
UsbHandlerProto.KERNEL_FUNCTION_LIST,
FileUtils.readTextFile(new File(FUNCTIONS_PATH), 0, null).trim());
+ } catch (FileNotFoundException exNotFound) {
+ Slog.w(TAG, "Ignore missing legacy kernel path in bugreport dump: "
+ + "kernel function list:" + FUNCTIONS_PATH);
} catch (Exception e) {
Slog.e(TAG, "Could not read kernel function list", e);
}
@@ -1301,6 +1392,20 @@
* Evaluates USB function policies and applies the change accordingly.
*/
protected abstract void setEnabledFunctions(long functions, boolean forceRestart);
+
+ public void setAccessoryUEventTime(long accessoryConnectionStartTime) {
+ mAccessoryConnectionStartTime = accessoryConnectionStartTime;
+ }
+
+ public void setStartAccessoryTrue() {
+ mStartAccessory = true;
+ }
+
+ public void resetUsbAccessoryHandshakeDebuggingInfo() {
+ mAccessoryConnectionStartTime = 0L;
+ mSendStringCount = 0;
+ mStartAccessory = false;
+ }
}
private static final class UsbHandlerLegacy extends UsbHandler {
diff --git a/telephony/java/android/telephony/CellLocation.java b/telephony/java/android/telephony/CellLocation.java
index 2d0bd52..4e4abd0 100644
--- a/telephony/java/android/telephony/CellLocation.java
+++ b/telephony/java/android/telephony/CellLocation.java
@@ -16,7 +16,9 @@
package android.telephony;
+import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
import android.os.Bundle;
import android.os.RemoteException;
import android.telephony.cdma.CdmaCellLocation;
@@ -31,11 +33,25 @@
public abstract class CellLocation {
/**
- * Request an update of the current location. If the location has changed,
- * a broadcast will be sent to everyone registered with {@link
- * PhoneStateListener#LISTEN_CELL_LOCATION}.
+ * This method will not do anything.
+ *
+ * Whenever location changes, a callback will automatically be be sent to
+ * all registrants of {@link PhoneStateListener#LISTEN_CELL_LOCATION}.
+ *
+ * <p>This method is a no-op for callers targeting SDK level 31 or greater.
+ * <p>This method is a no-op for callers that target SDK level 29 or 30 and lack
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
+ * <p>This method is a no-op for callers that target SDK level 28 or below and lack
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+ *
+ * Callers wishing to request a single location update should use
+ * {@link TelephonyManager#requestCellInfoUpdate}.
*/
public static void requestLocationUpdate() {
+ // Since this object doesn't have a context, this is the best we can do.
+ final Context appContext = ActivityThread.currentApplication();
+ if (appContext == null) return; // should never happen
+
try {
ITelephony phone = ITelephony.Stub.asInterface(
TelephonyFrameworkInitializer
@@ -43,7 +59,7 @@
.getTelephonyServiceRegisterer()
.get());
if (phone != null) {
- phone.updateServiceLocation();
+ phone.updateServiceLocationWithPackageName(appContext.getOpPackageName());
}
} catch (RemoteException ex) {
// ignore it
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 3104c20..d96e024 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2341,58 +2341,6 @@
}
/**
- * Enables location update notifications. {@link PhoneStateListener#onCellLocationChanged
- * PhoneStateListener.onCellLocationChanged} will be called on location updates.
- *
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES)
- public void enableLocationUpdates() {
- enableLocationUpdates(getSubId());
- }
-
- /**
- * Enables location update notifications for a subscription.
- * {@link PhoneStateListener#onCellLocationChanged
- * PhoneStateListener.onCellLocationChanged} will be called on location updates.
- *
- * @param subId for which the location updates are enabled
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES)
- public void enableLocationUpdates(int subId) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null)
- telephony.enableLocationUpdatesForSubscriber(subId);
- } catch (RemoteException ex) {
- } catch (NullPointerException ex) {
- }
- }
-
- /**
- * Disables location update notifications. {@link PhoneStateListener#onCellLocationChanged
- * PhoneStateListener.onCellLocationChanged} will be called on location updates.
- *
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES)
- public void disableLocationUpdates() {
- disableLocationUpdates(getSubId());
- }
-
- /** @hide */
- public void disableLocationUpdates(int subId) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null)
- telephony.disableLocationUpdatesForSubscriber(subId);
- } catch (RemoteException ex) {
- } catch (NullPointerException ex) {
- }
- }
-
- /**
* Returns the neighboring cell information of the device.
*
* @return List of NeighboringCellInfo or null if info unavailable.
@@ -9290,17 +9238,14 @@
return RADIO_POWER_UNAVAILABLE;
}
- /** @hide */
+ /**
+ * This method should not be used due to privacy and stability concerns.
+ *
+ * @hide
+ */
@SystemApi
- @SuppressLint("Doclava125")
public void updateServiceLocation() {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null)
- telephony.updateServiceLocation();
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#updateServiceLocation", e);
- }
+ Log.e(TAG, "Do not call TelephonyManager#updateServiceLocation()");
}
/** @hide */
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 4276e71..e2de5c8 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -222,42 +222,29 @@
boolean setRadioPower(boolean turnOn);
/**
- * Request to update location information in service state
+ * This method has been removed due to security and stability issues.
*/
@UnsupportedAppUsage
void updateServiceLocation();
/**
- * Request to update location information for a subscrition in service state
- * @param subId user preferred subId.
+ * Version of updateServiceLocation that records the caller and validates permissions.
*/
- void updateServiceLocationForSubscriber(int subId);
+ void updateServiceLocationWithPackageName(String callingPkg);
/**
- * Enable location update notifications.
+ * This method has been removed due to security and stability issues.
*/
@UnsupportedAppUsage
void enableLocationUpdates();
/**
- * Enable location update notifications.
- * @param subId user preferred subId.
- */
- void enableLocationUpdatesForSubscriber(int subId);
-
- /**
- * Disable location update notifications.
+ * This method has been removed due to security and stability issues.
*/
@UnsupportedAppUsage
void disableLocationUpdates();
/**
- * Disable location update notifications.
- * @param subId user preferred subId.
- */
- void disableLocationUpdatesForSubscriber(int subId);
-
- /**
* Allow mobile data connections.
*/
@UnsupportedAppUsage
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index d41a6c8..d216162 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -171,8 +171,8 @@
* if not specified </dd>
* </dl>
*
- * <p class="note">
- * Requires the READ_PHONE_STATE permission.
+ * <p class="note">This is a sticky broadcast, and therefore requires no permissions to listen
+ * to. Do not add any additional information to this broadcast.
*
* <p class="note">This is a protected intent that can only be sent
* by the system.
diff --git a/test-base/src/android/test/InstrumentationTestCase.java b/test-base/src/android/test/InstrumentationTestCase.java
index 6b79314..9f7a2fa 100644
--- a/test-base/src/android/test/InstrumentationTestCase.java
+++ b/test-base/src/android/test/InstrumentationTestCase.java
@@ -34,9 +34,9 @@
* A test case that has access to {@link Instrumentation}.
*
* @deprecated Use
- * <a href="{@docRoot}reference/android/support/test/InstrumentationRegistry.html">
+ * <a href="{@docRoot}reference/androidx/test/platform/app/InstrumentationRegistry.html">
* InstrumentationRegistry</a> instead. New tests should be written using the
- * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
+ * <a href="{@docRoot}training/testing/index.html">AndroidX Test Library</a>.
*/
@Deprecated
public class InstrumentationTestCase extends TestCase {
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/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
index 372765d..00da62f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
@@ -19,11 +19,9 @@
import static com.android.server.wm.flicker.CommonTransitions.appToSplitScreen;
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.LargeTest;
import org.junit.FixMethodOrder;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
@@ -36,8 +34,6 @@
@LargeTest
@RunWith(Parameterized.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 151632128)
-@Ignore("Waiting bug feedback")
public class OpenAppToSplitScreenTest extends NonRotationTestBase {
public OpenAppToSplitScreenTest(String beginRotationName, int beginRotation) {
@@ -67,15 +63,6 @@
}
@Test
- public void checkVisibility_dividerWindowBecomesVisible() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .hidesAboveAppWindow(DOCKED_STACK_DIVIDER)
- .then()
- .showsAboveAppWindow(DOCKED_STACK_DIVIDER)
- .forAllEntries());
- }
-
- @Test
public void checkVisibility_dividerLayerBecomesVisible() {
checkResults(result -> LayersTraceSubject.assertThat(result)
.hidesLayer(DOCKED_STACK_DIVIDER)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
index bd9529d..9e9c829 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
@@ -24,14 +24,12 @@
import android.view.Surface;
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import androidx.test.uiautomator.UiDevice;
import org.junit.AfterClass;
import org.junit.FixMethodOrder;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
@@ -43,8 +41,6 @@
@LargeTest
@RunWith(AndroidJUnit4.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 140856143)
-@Ignore("Waiting bug feedback")
public class SplitScreenToLauncherTest extends FlickerTestBase {
public SplitScreenToLauncherTest() {
@@ -95,15 +91,6 @@
.showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
}
- @Test
- public void checkVisibility_dividerWindowBecomesInVisible() {
- checkResults(result -> WmTraceSubject.assertThat(result)
- .showsAboveAppWindow(DOCKED_STACK_DIVIDER)
- .then()
- .hidesAboveAppWindow(DOCKED_STACK_DIVIDER)
- .forAllEntries());
- }
-
@AfterClass
public static void teardown() {
UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index 2be4ae6..a23df92 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -29,7 +29,12 @@
name: "StagedRollbackTest",
srcs: ["StagedRollbackTest/src/**/*.java"],
libs: ["tradefed"],
- static_libs: ["testng", "compatibility-tradefed", "RollbackTestLib"],
+ static_libs: [
+ "compatibility-tradefed",
+ "frameworks-base-hostutils",
+ "RollbackTestLib",
+ "testng",
+ ],
test_suites: ["general-tests"],
test_config: "StagedRollbackTest.xml",
data: [":com.android.apex.apkrollback.test_v1"],
@@ -39,7 +44,7 @@
name: "NetworkStagedRollbackTest",
srcs: ["NetworkStagedRollbackTest/src/**/*.java"],
libs: ["tradefed"],
- static_libs: ["RollbackTestLib"],
+ static_libs: ["RollbackTestLib", "frameworks-base-hostutils"],
test_suites: ["general-tests"],
test_config: "NetworkStagedRollbackTest.xml",
}
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..c2fd0c3 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",
@@ -449,6 +453,32 @@
after.forEach(dir -> assertDirectoryIsEmpty(dir));
}
+ @Test
+ public void testExpireApexRollback() throws Exception {
+ List<String> before = getSnapshotDirectories("/data/misc_ce/0/apexrollback");
+ pushTestApex();
+
+ // Push files to apex data directory
+ String oldFilePath1 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1;
+ String oldFilePath2 =
+ apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2;
+ assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1));
+ assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2));
+
+ // Install new version of the APEX with rollback enabled
+ runPhase("testRollbackApexDataDirectories_Phase1");
+ getDevice().reboot();
+
+ List<String> after = getSnapshotDirectories("/data/misc_ce/0/apexrollback");
+ // Only check directories newly created during the test
+ after.removeAll(before);
+ // Expire all rollbacks and check CE snapshot directories are deleted
+ runPhase("testCleanUp");
+ for (String dir : after) {
+ assertNull(getDevice().getFileEntry(dir));
+ }
+ }
+
private void pushTestApex() throws Exception {
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex";
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index c3fdd69..65d15a7 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -24,7 +24,7 @@
name: "StagedInstallInternalTest",
srcs: ["src/**/*.java"],
libs: ["tradefed"],
- static_libs: ["testng", "compatibility-tradefed"],
+ static_libs: ["testng", "compatibility-tradefed", "frameworks-base-hostutils"],
test_suites: ["general-tests"],
test_config: "StagedInstallInternalTest.xml",
}
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index 9b432f7..e6ba801 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -19,14 +19,17 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import com.android.ddmlib.Log;
+import com.android.tests.rollback.host.AbandonSessionsRule;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.ProcessInfo;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -35,7 +38,9 @@
private static final String TAG = StagedInstallInternalTest.class.getSimpleName();
private static final long SYSTEM_SERVER_TIMEOUT_MS = 60 * 1000;
- private boolean mWasRoot = false;
+
+ @Rule
+ public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this);
/**
* Runs the given phase of a test by calling into the device.
@@ -62,21 +67,11 @@
@Before
public void setUp() throws Exception {
- mWasRoot = getDevice().isAdbRoot();
- if (!mWasRoot) {
- getDevice().enableAdbRoot();
- }
cleanUp();
- // Abandon all staged sessions
- getDevice().executeShellCommand("pm install-abandon $(pm get-stagedsessions --only-ready "
- + "--only-parent --only-sessionid)");
}
@After
public void tearDown() throws Exception {
- if (!mWasRoot) {
- getDevice().disableAdbRoot();
- }
cleanUp();
}
@@ -89,23 +84,24 @@
private void restartSystemServer() throws Exception {
// Restart the system server
- long oldStartTime = getDevice().getProcessByName("system_server").getStartTime();
+ ProcessInfo oldPs = getDevice().getProcessByName("system_server");
+
+ getDevice().enableAdbRoot(); // Need root to restart system server
assertThat(getDevice().executeShellCommand("am restart")).contains("Restart the system");
+ getDevice().disableAdbRoot();
// Wait for new system server process to start
long start = System.currentTimeMillis();
- long newStartTime = oldStartTime;
while (System.currentTimeMillis() < start + SYSTEM_SERVER_TIMEOUT_MS) {
ProcessInfo newPs = getDevice().getProcessByName("system_server");
if (newPs != null) {
- newStartTime = newPs.getStartTime();
- if (newStartTime != oldStartTime) {
- break;
+ if (newPs.getPid() != oldPs.getPid()) {
+ getDevice().waitForDeviceAvailable();
+ return;
}
}
Thread.sleep(500);
}
- assertThat(newStartTime).isNotEqualTo(oldStartTime);
- getDevice().waitForDeviceAvailable();
+ fail("Timed out in restarting system server");
}
}
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index 9e3aeef..5a29c2c 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -114,6 +114,7 @@
@Mock private INetd mNetdService;
@Mock private PackageManagerInternal mMockPmi;
@Mock private UserManager mUserManager;
+ @Mock private PermissionMonitor.Dependencies mDeps;
private PermissionMonitor mPermissionMonitor;
@@ -128,7 +129,7 @@
new UserInfo(MOCK_USER2, "", 0),
}));
- mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService));
+ mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps));
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mMockPmi);
@@ -283,14 +284,14 @@
@Test
public void testHasRestrictedNetworkPermissionSystemUid() {
- doReturn(VERSION_P).when(mPermissionMonitor).getDeviceFirstSdkInt();
+ doReturn(VERSION_P).when(mDeps).getDeviceFirstSdkInt();
assertTrue(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
assertTrue(hasRestrictedNetworkPermission(
PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_INTERNAL));
assertTrue(hasRestrictedNetworkPermission(
PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
- doReturn(VERSION_Q).when(mPermissionMonitor).getDeviceFirstSdkInt();
+ doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt();
assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
assertFalse(hasRestrictedNetworkPermission(
PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_INTERNAL));
diff --git a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
index 16fed39..9adbf21 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
@@ -17,6 +17,7 @@
package com.android.server.net;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
@@ -31,6 +32,7 @@
import android.annotation.NonNull;
import android.content.Context;
import android.os.test.TestLooper;
+import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
@@ -61,7 +63,6 @@
private static final String TEST_IMSI3 = "466929999999999";
@Mock private Context mContext;
- @Mock private PhoneStateListener mPhoneStateListener;
@Mock private SubscriptionManager mSubscriptionManager;
@Mock private TelephonyManager mTelephonyManager;
@Mock private NetworkStatsSubscriptionsMonitor.Delegate mDelegate;
@@ -215,4 +216,53 @@
verify(mTelephonyManager, times(2)).listen(any(), eq(PhoneStateListener.LISTEN_NONE));
assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
}
+
+ @Test
+ public void test5g() {
+ mMonitor.start();
+ // Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback
+ // before changing RAT type. Also capture listener for later use.
+ addTestSub(TEST_SUBID1, TEST_IMSI1);
+ assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor =
+ ArgumentCaptor.forClass(RatTypeListener.class);
+ verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(),
+ eq(PhoneStateListener.LISTEN_SERVICE_STATE));
+ final RatTypeListener listener = CollectionUtils
+ .find(ratTypeListenerCaptor.getAllValues(), it -> it.getSubId() == TEST_SUBID1);
+ assertNotNull(listener);
+
+ // Set RAT type to 5G NSA (non-standalone) mode, verify the monitor outputs NETWORK_TYPE_NR.
+ final ServiceState serviceState = mock(ServiceState.class);
+ when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE);
+ when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED);
+ listener.onServiceStateChanged(serviceState);
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR);
+ reset(mDelegate);
+
+ // Set RAT type to LTE without NR connected, the RAT type should be downgraded to LTE.
+ when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_NONE);
+ listener.onServiceStateChanged(serviceState);
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_LTE);
+ reset(mDelegate);
+
+ // Verify NR connected with other RAT type does not take effect.
+ when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_UMTS);
+ when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED);
+ listener.onServiceStateChanged(serviceState);
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
+ reset(mDelegate);
+
+ // Set RAT type to 5G standalone mode, the RAT type should be NR.
+ setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
+ TelephonyManager.NETWORK_TYPE_NR);
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR);
+ reset(mDelegate);
+
+ // Set NR state to none in standalone mode does not change anything.
+ when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_NR);
+ when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_NONE);
+ listener.onServiceStateChanged(serviceState);
+ assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR);
+ }
}
diff --git a/tests/utils/hostutils/src/com/android/tests/rollback/host/AbandonSessionsRule.java b/tests/utils/hostutils/src/com/android/tests/rollback/host/AbandonSessionsRule.java
new file mode 100644
index 0000000..b086213
--- /dev/null
+++ b/tests/utils/hostutils/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/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index a230de4..4c741c4 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -25,6 +25,7 @@
namespace android {
namespace stats_log_api_gen {
+using google::protobuf::OneofDescriptor;
using google::protobuf::EnumDescriptor;
using google::protobuf::FieldDescriptor;
using google::protobuf::FileDescriptor;
@@ -396,16 +397,14 @@
collate_enums(*field->enum_type(), &atField);
}
- // Generate signature for pushed atoms
- if (atomDecl->code < PULL_ATOM_START_ID) {
- if (javaType == JAVA_TYPE_ENUM) {
- // All enums are treated as ints when it comes to function signatures.
- signature->push_back(JAVA_TYPE_INT);
- } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) {
- signature->push_back(JAVA_TYPE_BYTE_ARRAY);
- } else {
- signature->push_back(javaType);
- }
+ // Generate signature for atom.
+ if (javaType == JAVA_TYPE_ENUM) {
+ // All enums are treated as ints when it comes to function signatures.
+ signature->push_back(JAVA_TYPE_INT);
+ } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) {
+ signature->push_back(JAVA_TYPE_BYTE_ARRAY);
+ } else {
+ signature->push_back(javaType);
}
atomDecl->fields.push_back(atField);
@@ -518,8 +517,7 @@
shared_ptr<AtomDecl> atomDecl =
make_shared<AtomDecl>(atomField->number(), atomField->name(), atom->name());
- if (atomDecl->code < PULL_ATOM_START_ID &&
- atomField->options().GetExtension(os::statsd::truncate_timestamp)) {
+ if (atomField->options().GetExtension(os::statsd::truncate_timestamp)) {
addAnnotationToAtomDecl(atomDecl.get(), ATOM_ID_FIELD_NUMBER,
ANNOTATION_ID_TRUNCATE_TIMESTAMP, ANNOTATION_TYPE_BOOL,
AnnotationValue(true));
@@ -537,7 +535,24 @@
continue;
}
- FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = atoms->signatureInfoMap[signature];
+ const OneofDescriptor* oneofAtom = atomField->containing_oneof();
+ if (oneofAtom == nullptr) {
+ print_error(atomField, "Atom is not declared in a `oneof` field: %s\n",
+ atomField->name().c_str());
+ errorCount++;
+ continue;
+ }
+ else if ((oneofAtom->name() != ONEOF_PUSHED_ATOM_NAME) &&
+ (oneofAtom->name() != ONEOF_PULLED_ATOM_NAME)) {
+ print_error(atomField, "Atom is neither a pushed nor pulled atom: %s\n",
+ atomField->name().c_str());
+ errorCount++;
+ continue;
+ }
+
+ FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = oneofAtom->name() ==
+ ONEOF_PUSHED_ATOM_NAME ? atoms->signatureInfoMap[signature] :
+ atoms->pulledAtomsSignatureInfoMap[signature];
populateFieldNumberToAtomDeclSet(atomDecl, &fieldNumberToAtomDeclSet);
atoms->decls.insert(atomDecl);
@@ -556,6 +571,7 @@
}
if (dbg) {
+ // Signatures for pushed atoms.
printf("signatures = [\n");
for (SignatureInfoMap::const_iterator it = atoms->signatureInfoMap.begin();
it != atoms->signatureInfoMap.end(); it++) {
@@ -566,6 +582,17 @@
}
printf("\n");
}
+
+ // Signatures for pull atoms.
+ for (SignatureInfoMap::const_iterator it = atoms->pulledAtomsSignatureInfoMap.begin();
+ it != atoms->pulledAtomsSignatureInfoMap.end(); it++) {
+ printf(" ");
+ for (vector<java_type_t>::const_iterator jt = it->first.begin(); jt != it->first.end();
+ jt++) {
+ printf(" %d", (int)*jt);
+ }
+ printf("\n");
+ }
printf("]\n");
}
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index 10b34ec..e637ed9 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -29,6 +29,7 @@
namespace android {
namespace stats_log_api_gen {
+using google::protobuf::OneofDescriptor;
using google::protobuf::Descriptor;
using google::protobuf::FieldDescriptor;
using std::map;
@@ -41,6 +42,14 @@
const int FIRST_UID_IN_CHAIN_ID = 0;
+/**
+ * The types of oneof atoms.
+ *
+ * `OneofDescriptor::name()` returns the name of the oneof.
+ */
+const string ONEOF_PUSHED_ATOM_NAME = "pushed";
+const string ONEOF_PULLED_ATOM_NAME = "pulled";
+
enum AnnotationId : uint8_t {
ANNOTATION_ID_IS_UID = 1,
ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2,
@@ -184,6 +193,7 @@
struct Atoms {
SignatureInfoMap signatureInfoMap;
+ SignatureInfoMap pulledAtomsSignatureInfoMap;
AtomDeclSet decls;
AtomDeclSet non_chained_decls;
SignatureInfoMap nonChainedSignatureInfoMap;
diff --git a/tools/stats_log_api_gen/java_writer.cpp b/tools/stats_log_api_gen/java_writer.cpp
index f4c937c..ffbe9f8 100644
--- a/tools/stats_log_api_gen/java_writer.cpp
+++ b/tools/stats_log_api_gen/java_writer.cpp
@@ -96,29 +96,160 @@
}
}
+static void write_method_signature(FILE* out, const vector<java_type_t>& signature,
+ const AtomDecl& attributionDecl) {
+ int argIndex = 1;
+ for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+ arg++) {
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ fprintf(out, ", %s[] %s", java_type_name(chainField.javaType),
+ chainField.name.c_str());
+ }
+ } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+ fprintf(out, ", android.util.SparseArray<Object> valueMap");
+ } else {
+ fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
+ }
+ argIndex++;
+ }
+}
+
+static int write_method_body(FILE* out, const vector<java_type_t>& signature,
+ const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet,
+ const AtomDecl& attributionDecl, const string& indent) {
+ // Start StatsEvent.Builder.
+ fprintf(out,
+ "%s final StatsEvent.Builder builder = "
+ "StatsEvent.newBuilder();\n",
+ indent.c_str());
+
+ // Write atom code.
+ fprintf(out, "%s builder.setAtomId(code);\n", indent.c_str());
+ write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet);
+
+ // Write the args.
+ int argIndex = 1;
+ for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+ arg++) {
+ switch (*arg) {
+ case JAVA_TYPE_BOOLEAN:
+ fprintf(out, "%s builder.writeBoolean(arg%d);\n", indent.c_str(),
+ argIndex);
+ break;
+ case JAVA_TYPE_INT:
+ case JAVA_TYPE_ENUM:
+ fprintf(out, "%s builder.writeInt(arg%d);\n", indent.c_str(), argIndex);
+ break;
+ case JAVA_TYPE_FLOAT:
+ fprintf(out, "%s builder.writeFloat(arg%d);\n", indent.c_str(),
+ argIndex);
+ break;
+ case JAVA_TYPE_LONG:
+ fprintf(out, "%s builder.writeLong(arg%d);\n", indent.c_str(), argIndex);
+ break;
+ case JAVA_TYPE_STRING:
+ fprintf(out, "%s builder.writeString(arg%d);\n", indent.c_str(),
+ argIndex);
+ break;
+ case JAVA_TYPE_BYTE_ARRAY:
+ fprintf(out,
+ "%s builder.writeByteArray(null == arg%d ? new byte[0] : "
+ "arg%d);\n",
+ indent.c_str(), argIndex, argIndex);
+ break;
+ case JAVA_TYPE_ATTRIBUTION_CHAIN: {
+ const char* uidName = attributionDecl.fields.front().name.c_str();
+ const char* tagName = attributionDecl.fields.back().name.c_str();
+
+ fprintf(out, "%s builder.writeAttributionChain(\n", indent.c_str());
+ fprintf(out, "%s null == %s ? new int[0] : %s,\n",
+ indent.c_str(), uidName, uidName);
+ fprintf(out, "%s null == %s ? new String[0] : %s);\n",
+ indent.c_str(), tagName, tagName);
+ break;
+ }
+ case JAVA_TYPE_KEY_VALUE_PAIR:
+ fprintf(out, "\n");
+ fprintf(out, "%s // Write KeyValuePairs.\n", indent.c_str());
+ fprintf(out, "%s final int count = valueMap.size();\n", indent.c_str());
+ fprintf(out, "%s android.util.SparseIntArray intMap = null;\n",
+ indent.c_str());
+ fprintf(out, "%s android.util.SparseLongArray longMap = null;\n",
+ indent.c_str());
+ fprintf(out, "%s android.util.SparseArray<String> stringMap = null;\n",
+ indent.c_str());
+ fprintf(out, "%s android.util.SparseArray<Float> floatMap = null;\n",
+ indent.c_str());
+ fprintf(out, "%s for (int i = 0; i < count; i++) {\n", indent.c_str());
+ fprintf(out, "%s final int key = valueMap.keyAt(i);\n",
+ indent.c_str());
+ fprintf(out, "%s final Object value = valueMap.valueAt(i);\n",
+ indent.c_str());
+ fprintf(out, "%s if (value instanceof Integer) {\n", indent.c_str());
+ fprintf(out, "%s if (null == intMap) {\n", indent.c_str());
+ fprintf(out,
+ "%s intMap = new "
+ "android.util.SparseIntArray();\n",
+ indent.c_str());
+ fprintf(out, "%s }\n", indent.c_str());
+ fprintf(out, "%s intMap.put(key, (Integer) value);\n",
+ indent.c_str());
+ fprintf(out, "%s } else if (value instanceof Long) {\n",
+ indent.c_str());
+ fprintf(out, "%s if (null == longMap) {\n", indent.c_str());
+ fprintf(out,
+ "%s longMap = new "
+ "android.util.SparseLongArray();\n",
+ indent.c_str());
+ fprintf(out, "%s }\n", indent.c_str());
+ fprintf(out, "%s longMap.put(key, (Long) value);\n",
+ indent.c_str());
+ fprintf(out, "%s } else if (value instanceof String) {\n",
+ indent.c_str());
+ fprintf(out, "%s if (null == stringMap) {\n", indent.c_str());
+ fprintf(out,
+ "%s stringMap = new "
+ "android.util.SparseArray<>();\n",
+ indent.c_str());
+ fprintf(out, "%s }\n", indent.c_str());
+ fprintf(out, "%s stringMap.put(key, (String) value);\n",
+ indent.c_str());
+ fprintf(out, "%s } else if (value instanceof Float) {\n",
+ indent.c_str());
+ fprintf(out, "%s if (null == floatMap) {\n", indent.c_str());
+ fprintf(out,
+ "%s floatMap = new "
+ "android.util.SparseArray<>();\n",
+ indent.c_str());
+ fprintf(out, "%s }\n", indent.c_str());
+ fprintf(out, "%s floatMap.put(key, (Float) value);\n",
+ indent.c_str());
+ fprintf(out, "%s }\n", indent.c_str());
+ fprintf(out, "%s }\n", indent.c_str());
+ fprintf(out,
+ "%s builder.writeKeyValuePairs("
+ "intMap, longMap, stringMap, floatMap);\n",
+ indent.c_str());
+ break;
+ default:
+ // Unsupported types: OBJECT, DOUBLE.
+ fprintf(stderr, "Encountered unsupported type.");
+ return 1;
+ }
+ write_annotations(out, argIndex, fieldNumberToAtomDeclSet);
+ argIndex++;
+ }
+ return 0;
+}
+
static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMap,
const AtomDecl& attributionDecl, const bool supportQ) {
for (auto signatureInfoMapIt = signatureInfoMap.begin();
signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
// Print method signature.
fprintf(out, " public static void write(int code");
- const vector<java_type_t>& signature = signatureInfoMapIt->first;
- const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = signatureInfoMapIt->second;
- int argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
- arg++) {
- if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- for (auto chainField : attributionDecl.fields) {
- fprintf(out, ", %s[] %s", java_type_name(chainField.javaType),
- chainField.name.c_str());
- }
- } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
- fprintf(out, ", android.util.SparseArray<Object> valueMap");
- } else {
- fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
- }
- argIndex++;
- }
+ write_method_signature(out, signatureInfoMapIt->first, attributionDecl);
fprintf(out, ") {\n");
// Print method body.
@@ -128,130 +259,13 @@
indent = " ";
}
- // Start StatsEvent.Builder.
- fprintf(out,
- "%s final StatsEvent.Builder builder = "
- "StatsEvent.newBuilder();\n",
- indent.c_str());
-
- // Write atom code.
- fprintf(out, "%s builder.setAtomId(code);\n", indent.c_str());
- write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet);
-
- // Write the args.
- argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
- arg++) {
- switch (*arg) {
- case JAVA_TYPE_BOOLEAN:
- fprintf(out, "%s builder.writeBoolean(arg%d);\n", indent.c_str(),
- argIndex);
- break;
- case JAVA_TYPE_INT:
- case JAVA_TYPE_ENUM:
- fprintf(out, "%s builder.writeInt(arg%d);\n", indent.c_str(), argIndex);
- break;
- case JAVA_TYPE_FLOAT:
- fprintf(out, "%s builder.writeFloat(arg%d);\n", indent.c_str(),
- argIndex);
- break;
- case JAVA_TYPE_LONG:
- fprintf(out, "%s builder.writeLong(arg%d);\n", indent.c_str(), argIndex);
- break;
- case JAVA_TYPE_STRING:
- fprintf(out, "%s builder.writeString(arg%d);\n", indent.c_str(),
- argIndex);
- break;
- case JAVA_TYPE_BYTE_ARRAY:
- fprintf(out,
- "%s builder.writeByteArray(null == arg%d ? new byte[0] : "
- "arg%d);\n",
- indent.c_str(), argIndex, argIndex);
- break;
- case JAVA_TYPE_ATTRIBUTION_CHAIN: {
- const char* uidName = attributionDecl.fields.front().name.c_str();
- const char* tagName = attributionDecl.fields.back().name.c_str();
-
- fprintf(out, "%s builder.writeAttributionChain(\n", indent.c_str());
- fprintf(out, "%s null == %s ? new int[0] : %s,\n",
- indent.c_str(), uidName, uidName);
- fprintf(out, "%s null == %s ? new String[0] : %s);\n",
- indent.c_str(), tagName, tagName);
- break;
- }
- case JAVA_TYPE_KEY_VALUE_PAIR:
- fprintf(out, "\n");
- fprintf(out, "%s // Write KeyValuePairs.\n", indent.c_str());
- fprintf(out, "%s final int count = valueMap.size();\n", indent.c_str());
- fprintf(out, "%s android.util.SparseIntArray intMap = null;\n",
- indent.c_str());
- fprintf(out, "%s android.util.SparseLongArray longMap = null;\n",
- indent.c_str());
- fprintf(out, "%s android.util.SparseArray<String> stringMap = null;\n",
- indent.c_str());
- fprintf(out, "%s android.util.SparseArray<Float> floatMap = null;\n",
- indent.c_str());
- fprintf(out, "%s for (int i = 0; i < count; i++) {\n", indent.c_str());
- fprintf(out, "%s final int key = valueMap.keyAt(i);\n",
- indent.c_str());
- fprintf(out, "%s final Object value = valueMap.valueAt(i);\n",
- indent.c_str());
- fprintf(out, "%s if (value instanceof Integer) {\n", indent.c_str());
- fprintf(out, "%s if (null == intMap) {\n", indent.c_str());
- fprintf(out,
- "%s intMap = new "
- "android.util.SparseIntArray();\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s intMap.put(key, (Integer) value);\n",
- indent.c_str());
- fprintf(out, "%s } else if (value instanceof Long) {\n",
- indent.c_str());
- fprintf(out, "%s if (null == longMap) {\n", indent.c_str());
- fprintf(out,
- "%s longMap = new "
- "android.util.SparseLongArray();\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s longMap.put(key, (Long) value);\n",
- indent.c_str());
- fprintf(out, "%s } else if (value instanceof String) {\n",
- indent.c_str());
- fprintf(out, "%s if (null == stringMap) {\n", indent.c_str());
- fprintf(out,
- "%s stringMap = new "
- "android.util.SparseArray<>();\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s stringMap.put(key, (String) value);\n",
- indent.c_str());
- fprintf(out, "%s } else if (value instanceof Float) {\n",
- indent.c_str());
- fprintf(out, "%s if (null == floatMap) {\n", indent.c_str());
- fprintf(out,
- "%s floatMap = new "
- "android.util.SparseArray<>();\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s floatMap.put(key, (Float) value);\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out,
- "%s builder.writeKeyValuePairs("
- "intMap, longMap, stringMap, floatMap);\n",
- indent.c_str());
- break;
- default:
- // Unsupported types: OBJECT, DOUBLE.
- fprintf(stderr, "Encountered unsupported type.");
- return 1;
- }
- write_annotations(out, argIndex, fieldNumberToAtomDeclSet);
- argIndex++;
+ int ret = write_method_body(out, signatureInfoMapIt->first, signatureInfoMapIt->second,
+ attributionDecl, indent);
+ if (ret != 0) {
+ return ret;
}
-
fprintf(out, "\n");
+
fprintf(out, "%s builder.usePooledBuffer();\n", indent.c_str());
fprintf(out, "%s StatsLog.write(builder.build());\n", indent.c_str());
@@ -259,9 +273,9 @@
if (supportQ) {
fprintf(out, " } else {\n");
fprintf(out, " QLogger.write(code");
- argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature.begin();
- arg != signature.end(); arg++) {
+ int argIndex = 1;
+ for (vector<java_type_t>::const_iterator arg = signatureInfoMapIt->first.begin();
+ arg != signatureInfoMapIt->first.end(); arg++) {
if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
const char* uidName = attributionDecl.fields.front().name.c_str();
const char* tagName = attributionDecl.fields.back().name.c_str();
@@ -285,6 +299,32 @@
return 0;
}
+static int write_java_build_stats_event_methods(FILE* out, const SignatureInfoMap& signatureInfoMap,
+ const AtomDecl& attributionDecl) {
+ for (auto signatureInfoMapIt = signatureInfoMap.begin();
+ signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
+ // Print method signature.
+ fprintf(out, " public static StatsEvent buildStatsEvent(int code");
+ write_method_signature(out, signatureInfoMapIt->first, attributionDecl);
+ fprintf(out, ") {\n");
+
+ // Print method body.
+ string indent("");
+ int ret = write_method_body(out, signatureInfoMapIt->first, signatureInfoMapIt->second,
+ attributionDecl, indent);
+ if (ret != 0) {
+ return ret;
+ }
+ fprintf(out, "\n");
+
+ fprintf(out, "%s return builder.build();\n", indent.c_str());
+
+ fprintf(out, " }\n"); // method
+ fprintf(out, "\n");
+ }
+ return 0;
+}
+
int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
const string& javaClass, const string& javaPackage, const bool supportQ,
const bool supportWorkSource) {
@@ -319,6 +359,8 @@
fprintf(out, " // Write methods\n");
errors += write_java_methods(out, atoms.signatureInfoMap, attributionDecl, supportQ);
errors += write_java_non_chained_methods(out, atoms.nonChainedSignatureInfoMap);
+ errors += write_java_build_stats_event_methods(out, atoms.pulledAtomsSignatureInfoMap,
+ attributionDecl);
if (supportWorkSource) {
errors += write_java_work_source_methods(out, atoms.signatureInfoMap);
}
diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto
index aaa488e..e658b62 100644
--- a/tools/stats_log_api_gen/test.proto
+++ b/tools/stats_log_api_gen/test.proto
@@ -58,7 +58,7 @@
}
message Event {
- oneof event {
+ oneof pushed {
OutOfOrderAtom out_of_order_atom = 2;
IntAtom int_atom = 1;
AnotherIntAtom another_int_atom = 3;
@@ -74,7 +74,7 @@
}
message BadTypesEvent {
- oneof event {
+ oneof pushed {
BadTypesAtom bad_types_atom = 1;
}
}
@@ -84,7 +84,7 @@
}
message BadSkippedFieldSingle {
- oneof event {
+ oneof pushed {
BadSkippedFieldSingleAtom bad = 1;
}
}
@@ -96,7 +96,7 @@
}
message BadSkippedFieldMultiple {
- oneof event {
+ oneof pushed {
BadSkippedFieldMultipleAtom bad = 1;
}
}
@@ -107,11 +107,11 @@
}
message BadAttributionNodePosition {
- oneof event { BadAttributionNodePositionAtom bad = 1; }
+ oneof pushed { BadAttributionNodePositionAtom bad = 1; }
}
message GoodEventWithBinaryFieldAtom {
- oneof event { GoodBinaryFieldAtom field1 = 1; }
+ oneof pushed { GoodBinaryFieldAtom field1 = 1; }
}
message ComplexField {
@@ -124,7 +124,7 @@
}
message BadEventWithBinaryFieldAtom {
- oneof event { BadBinaryFieldAtom field1 = 1; }
+ oneof pushed { BadBinaryFieldAtom field1 = 1; }
}
message BadBinaryFieldAtom {
@@ -133,7 +133,7 @@
}
message BadStateAtoms {
- oneof event {
+ oneof pushed {
BadStateAtom1 bad1 = 1;
BadStateAtom2 bad2 = 2;
BadStateAtom3 bad3 = 3;
@@ -141,7 +141,7 @@
}
message GoodStateAtoms {
- oneof event {
+ oneof pushed {
GoodStateAtom1 good1 = 1;
GoodStateAtom2 good2 = 2;
}
@@ -204,7 +204,7 @@
}
message ModuleAtoms {
- oneof event {
+ oneof pushed {
ModuleOneAtom module_one_atom = 1 [(android.os.statsd.module) = "module1"];
ModuleTwoAtom module_two_atom = 2 [(android.os.statsd.module) = "module2"];
ModuleOneAndTwoAtom module_one_and_two_atom = 3 [
@@ -213,3 +213,24 @@
NoModuleAtom no_module_atom = 4;
}
}
+
+message NotAPushNorPullAtom {
+ oneof event {
+ IntAtom int_atom = 1;
+ }
+}
+
+message AtomNotInAOneof {
+ optional IntAtom int_atom = 1;
+}
+
+message PushedAndPulledAtoms {
+ oneof pushed {
+ IntAtom int_atom_1 = 1;
+ }
+
+ oneof pulled {
+ OutOfOrderAtom out_of_order_atom = 11;
+ AnotherIntAtom another_int_atom = 10;
+ }
+}
diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp
index dbae588..5fd728a 100644
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ b/tools/stats_log_api_gen/test_collation.cpp
@@ -365,5 +365,69 @@
EXPECT_TRUE(annotation->value.boolValue);
}
+/**
+ * Test that an atom is not a pushed nor pulled atom.
+ */
+TEST(CollationTest, InvalidAtomType) {
+ Atoms atoms;
+ int errorCount = collate_atoms(NotAPushNorPullAtom::descriptor(), DEFAULT_MODULE_NAME, &atoms);
+
+ EXPECT_EQ(1, errorCount);
+}
+
+/**
+ * Test that an atom was not declared in a `oneof` field.
+ */
+TEST(CollationTest, AtomNotDeclaredInAOneof) {
+ Atoms atoms;
+ int errorCount = collate_atoms(AtomNotInAOneof::descriptor(), DEFAULT_MODULE_NAME, &atoms);
+
+ EXPECT_EQ(1, errorCount);
+}
+
+/**
+ * Test a correct collation with pushed and pulled atoms.
+ */
+TEST(CollationTest, CollatePushedAndPulledAtoms) {
+ Atoms atoms;
+ int errorCount = collate_atoms(PushedAndPulledAtoms::descriptor(), DEFAULT_MODULE_NAME, &atoms);
+
+ EXPECT_EQ(0, errorCount);
+ EXPECT_EQ(1ul, atoms.signatureInfoMap.size());
+ EXPECT_EQ(2ul, atoms.pulledAtomsSignatureInfoMap.size());
+
+ // IntAtom
+ EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap, JAVA_TYPE_INT);
+
+ // AnotherIntAtom
+ EXPECT_MAP_CONTAINS_SIGNATURE(atoms.pulledAtomsSignatureInfoMap, JAVA_TYPE_INT);
+
+ // OutOfOrderAtom
+ EXPECT_MAP_CONTAINS_SIGNATURE(atoms.pulledAtomsSignatureInfoMap, JAVA_TYPE_INT, JAVA_TYPE_INT);
+
+ EXPECT_EQ(3ul, atoms.decls.size());
+
+ AtomDeclSet::const_iterator atomIt = atoms.decls.begin();
+ EXPECT_EQ(1, (*atomIt)->code);
+ EXPECT_EQ("int_atom_1", (*atomIt)->name);
+ EXPECT_EQ("IntAtom", (*atomIt)->message);
+ EXPECT_NO_ENUM_FIELD((*atomIt));
+ atomIt++;
+
+ EXPECT_EQ(10, (*atomIt)->code);
+ EXPECT_EQ("another_int_atom", (*atomIt)->name);
+ EXPECT_EQ("AnotherIntAtom", (*atomIt)->message);
+ EXPECT_NO_ENUM_FIELD((*atomIt));
+ atomIt++;
+
+ EXPECT_EQ(11, (*atomIt)->code);
+ EXPECT_EQ("out_of_order_atom", (*atomIt)->name);
+ EXPECT_EQ("OutOfOrderAtom", (*atomIt)->message);
+ EXPECT_NO_ENUM_FIELD((*atomIt));
+ atomIt++;
+
+ EXPECT_EQ(atoms.decls.end(), atomIt);
+}
+
} // namespace stats_log_api_gen
} // namespace android