Merge "Make resource multilocale depend on default attribute" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 6d74a84..b924ac8 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -22,6 +22,7 @@
":android.os.flags-aconfig-java{.generated_srcjars}",
":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
":android.security.flags-aconfig-java{.generated_srcjars}",
+ ":android.service.notification.flags-aconfig-java{.generated_srcjars}",
":android.view.flags-aconfig-java{.generated_srcjars}",
":android.view.accessibility.flags-aconfig-java{.generated_srcjars}",
":camera_platform_flags_core_java_lib{.generated_srcjars}",
@@ -51,6 +52,7 @@
":aconfig_midi_flags_java_lib{.generated_srcjars}",
":android.service.autofill.flags-aconfig-java{.generated_srcjars}",
":com.android.net.flags-aconfig-java{.generated_srcjars}",
+ ":device_policy_aconfig_flags_lib{.generated_srcjars}",
]
filegroup {
@@ -523,3 +525,36 @@
aconfig_declarations: "com.android.net.flags-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// DevicePolicy
+aconfig_declarations {
+ name: "device_policy_aconfig_flags",
+ package: "android.app.admin.flags",
+ srcs: [
+ "core/java/android/app/admin/flags/flags.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "device_policy_aconfig_flags_lib",
+ aconfig_declarations: "device_policy_aconfig_flags",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+cc_aconfig_library {
+ name: "device_policy_aconfig_flags_c_lib",
+ aconfig_declarations: "device_policy_aconfig_flags",
+}
+
+// Notifications
+aconfig_declarations {
+ name: "android.service.notification.flags-aconfig",
+ package: "android.service.notification",
+ srcs: ["core/java/android/service/notification/flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.service.notification.flags-aconfig-java",
+ aconfig_declarations: "android.service.notification.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/api/javadoc-lint-baseline b/api/javadoc-lint-baseline
index d9e72b8..29a8dfa 100644
--- a/api/javadoc-lint-baseline
+++ b/api/javadoc-lint-baseline
@@ -1,58 +1,4 @@
-android/adservices/ondevicepersonalization/DownloadCompletedInput.java:22: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onDownloadCompleted() IsolatedWorker#onDownloadCompleted()" in android.adservices.ondevicepersonalization.DownloadCompletedInput [101]
-android/adservices/ondevicepersonalization/DownloadCompletedOutput.java:21: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onDownloadCompleted() IsolatedWorker#onDownloadCompleted()" in android.adservices.ondevicepersonalization.DownloadCompletedOutput [101]
-android/adservices/ondevicepersonalization/EventLogRecord.java:13: lint: Unresolved link/see tag "RequestRecordRecord" in android.adservices.ondevicepersonalization.EventLogRecord [101]
-android/adservices/ondevicepersonalization/EventUrlProvider.java:43: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onEvent IsolatedWorker#onEvent" in android.adservices.ondevicepersonalization.EventUrlProvider [101]
-android/adservices/ondevicepersonalization/ExecuteInput.java:22: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.ExecuteInput [101]
-android/adservices/ondevicepersonalization/ExecuteOutput.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.ExecuteOutput [101]
-android/adservices/ondevicepersonalization/ExecuteOutput.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.OnDevicePersonalizationManager#execute() OnDevicePersonalizationManager#execute()" in android.adservices.ondevicepersonalization.ExecuteOutput [101]
-android/adservices/ondevicepersonalization/ExecuteOutput.java:31: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.ExecuteOutput [101]
-android/adservices/ondevicepersonalization/ExecuteOutput.java:93: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.ExecuteOutput.Builder [101]
-android/adservices/ondevicepersonalization/IsolatedService.java:18: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.IsolatedService [101]
-android/adservices/ondevicepersonalization/IsolatedService.java:18: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.OnDevicePersonalizationManager#execute" in android.adservices.ondevicepersonalization.IsolatedService [101]
-android/adservices/ondevicepersonalization/IsolatedService.java:119: lint: Unresolved link/see tag "IsolatedCmputationCallback#onWebViewEvent()" in android.adservices.ondevicepersonalization.IsolatedService [101]
-android/adservices/ondevicepersonalization/IsolatedService.java:119: lint: Unresolved link/see tag "IsolatedCmputationCallback#onEvent()" in android.adservices.ondevicepersonalization.IsolatedService [101]
-android/adservices/ondevicepersonalization/IsolatedService.java:119: lint: Unresolved link/see tag "WebView" in android.adservices.ondevicepersonalization.IsolatedService [101]
-android/adservices/ondevicepersonalization/IsolatedWorker.java:9: lint: Unresolved link/see tag "RunTimeException" in android.adservices.ondevicepersonalization.IsolatedWorker [101]
-android/adservices/ondevicepersonalization/IsolatedWorker.java:24: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.OnDevicePersonalizationManager#execute" in android.adservices.ondevicepersonalization.IsolatedWorker [101]
-android/adservices/ondevicepersonalization/IsolatedWorker.java:57: lint: Unresolved link/see tag "#onExecute()" in android.adservices.ondevicepersonalization.IsolatedWorker [101]
-android/adservices/ondevicepersonalization/IsolatedWorker.java:74: lint: Unresolved link/see tag "#onRender()" in android.adservices.ondevicepersonalization.IsolatedWorker [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:-11: lint: Unresolved link/see tag "requestSurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:11: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:11: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedService#onExecute() IsolatedService#onExecute()" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:19: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:54: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:54: lint: Unresolved link/see tag "SurfaceView#getHostToken()" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:54: lint: Unresolved link/see tag "execute" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:60: lint: Unresolved link/see tag "#execute()" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:60: lint: Unresolved link/see tag "SurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:60: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:60: lint: Unresolved link/see tag "View" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:64: lint: Unresolved link/see tag "SurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:69: lint: Unresolved link/see tag "SurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:70: lint: Unresolved link/see tag "SurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/RenderInput.java:21: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onRender() IsolatedWorker#onRender()" in android.adservices.ondevicepersonalization.RenderInput [101]
-android/adservices/ondevicepersonalization/RenderInput.java:53: lint: Unresolved link/see tag "onExecute" in android.adservices.ondevicepersonalization.RenderInput [101]
-android/adservices/ondevicepersonalization/RenderOutput.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.RenderOutput [101]
-android/adservices/ondevicepersonalization/RenderOutput.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.OnDevicePersonalizationManager#requestSurfacePackage() OnDevicePersonalizationManager#requestSurfacePackage()" in android.adservices.ondevicepersonalization.RenderOutput [101]
-android/adservices/ondevicepersonalization/RenderOutput.java:31: lint: Unresolved link/see tag "getTemplateId()" in android.adservices.ondevicepersonalization.RenderOutput [101]
-android/adservices/ondevicepersonalization/RenderOutput.java:31: lint: Unresolved link/see tag "getTemplateParams" in android.adservices.ondevicepersonalization.RenderOutput [101]
-android/adservices/ondevicepersonalization/RenderOutput.java:41: lint: Unresolved link/see tag "getContent()" in android.adservices.ondevicepersonalization.RenderOutput [101]
-android/adservices/ondevicepersonalization/RenderOutput.java:52: lint: Unresolved link/see tag "getTemplateId()" in android.adservices.ondevicepersonalization.RenderOutput [101]
-android/adservices/ondevicepersonalization/RenderOutput.java:102: lint: Unresolved link/see tag "getTemplateId()" in android.adservices.ondevicepersonalization.RenderOutput.Builder [101]
-android/adservices/ondevicepersonalization/RenderOutput.java:102: lint: Unresolved link/see tag "getTemplateParams" in android.adservices.ondevicepersonalization.RenderOutput.Builder [101]
-android/adservices/ondevicepersonalization/RenderOutput.java:114: lint: Unresolved link/see tag "getContent()" in android.adservices.ondevicepersonalization.RenderOutput.Builder [101]
-android/adservices/ondevicepersonalization/RenderOutput.java:127: lint: Unresolved link/see tag "getTemplateId()" in android.adservices.ondevicepersonalization.RenderOutput.Builder [101]
-android/adservices/ondevicepersonalization/RenderingConfig.java:20: lint: Unresolved link/see tag "View" in android.adservices.ondevicepersonalization.RenderingConfig [101]
-android/adservices/ondevicepersonalization/RenderingConfig.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.RenderingConfig [101]
-android/adservices/ondevicepersonalization/RenderingConfig.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onRender() IsolatedWorker#onRender()" in android.adservices.ondevicepersonalization.RenderingConfig [101]
-android/adservices/ondevicepersonalization/RenderingConfig.java:33: lint: Unresolved link/see tag "IsolatedSurface#getRemoteData" in android.adservices.ondevicepersonalization.RenderingConfig [101]
-android/adservices/ondevicepersonalization/RenderingConfig.java:85: lint: Unresolved link/see tag "IsolatedSurface#getRemoteData" in android.adservices.ondevicepersonalization.RenderingConfig.Builder [101]
-android/adservices/ondevicepersonalization/RequestLogRecord.java:19: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.RequestLogRecord [101]
-android/adservices/ondevicepersonalization/SurfacePackageToken.java:20: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.SurfacePackageToken [101]
-android/adservices/ondevicepersonalization/WebViewEventInput.java:21: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onWebViewEvent() IsolatedWorker#onWebViewEvent()" in android.adservices.ondevicepersonalization.WebViewEventInput [101]
-android/adservices/ondevicepersonalization/WebViewEventInput.java:30: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.WebViewEventInput [101]
-android/adservices/ondevicepersonalization/WebViewEventInput.java:41: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.EventUrlProvider#createEventTrackingUrlWithResponse() EventUrlProvider#createEventTrackingUrlWithResponse()" in android.adservices.ondevicepersonalization.WebViewEventInput [101]
-android/adservices/ondevicepersonalization/WebViewEventOutput.java:21: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onWebViewEvent() IsolatedWorker#onWebViewEvent()" in android.adservices.ondevicepersonalization.WebViewEventOutput [101]
+// b/305195721
android/app/admin/DevicePolicyManager.java:2670: lint: Unresolved link/see tag "android.os.UserManager#DISALLOW_CAMERA UserManager#DISALLOW_CAMERA" in android.app.admin.DevicePolicyManager [101]
android/app/admin/DevicePolicyManager.java:7257: lint: Unresolved link/see tag "android.app.admin.DevicePolicyIdentifiers#USB_DATA_SIGNALING_POLICY DevicePolicyIdentifiers#USB_DATA_SIGNALING_POLICY" in android.app.admin.DevicePolicyManager [101]
android/app/admin/DevicePolicyManager.java:7425: lint: Unresolved link/see tag "ACTION_DEVICE_FINANCING_STATE_CHANGED" in android.app.admin.DevicePolicyManager [101]
@@ -61,6 +7,8 @@
android/app/admin/DevicePolicyManager.java:8860: lint: Unresolved link/see tag "android.app.admin.DevicePolicyResources.Drawables DevicePolicyResources.Drawables" in android.app.admin.DevicePolicyManager [101]
android/app/admin/DevicePolicyManager.java:8860: lint: Unresolved link/see tag "android.app.admin.DevicePolicyResources.Strings DevicePolicyResources.Strings" in android.app.admin.DevicePolicyManager [101]
android/app/admin/DevicePolicyResourcesManager.java:179: lint: Unresolved link/see tag "android.app.admin.DevicePolicyResources.Strings DevicePolicyResources.Strings" in android.app.admin.DevicePolicyResourcesManager [101]
+
+// b/303477132
android/app/appsearch/AppSearchSchema.java:402: lint: Unresolved link/see tag "#getIndexableNestedProperties()" in android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder [101]
android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#LIST_FILTER_QUERY_LANGUAGE" in android.app.appsearch.AppSearchSession [101]
android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#NUMERIC_SEARCH" in android.app.appsearch.AppSearchSession [101]
@@ -73,6 +21,8 @@
android/app/appsearch/SearchSpec.java:913: lint: Unresolved link/see tag "Features#NUMERIC_SEARCH" in android.app.appsearch.SearchSpec.Builder [101]
android/app/appsearch/SearchSpec.java:925: lint: Unresolved link/see tag "Features#VERBATIM_SEARCH" in android.app.appsearch.SearchSpec.Builder [101]
android/app/appsearch/SearchSpec.java:929: lint: Unresolved link/see tag "Features#LIST_FILTER_QUERY_LANGUAGE" in android.app.appsearch.SearchSpec.Builder [101]
+
+// b/303582215
android/hardware/camera2/CameraCharacteristics.java:2169: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101]
android/hardware/camera2/CameraCharacteristics.java:2344: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#concurrent-stream-guaranteed-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101]
android/hardware/camera2/CameraCharacteristics.java:2344: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#concurrent-stream-guaranteed-configurations tables" in android.hardware.camera2.CameraCharacteristics [101]
@@ -98,77 +48,37 @@
android/hardware/camera2/CaptureRequest.java:1501: lint: Unresolved link/see tag "SessionConfiguration#setSessionParameters" in android.hardware.camera2.CaptureRequest [101]
android/hardware/camera2/CaptureResult.java:923: lint: Unresolved link/see tag "SessionConfiguration#setSessionParameters" in android.hardware.camera2.CaptureResult [101]
android/hardware/camera2/CaptureResult.java:2337: lint: Unresolved link/see tag "SessionConfiguration#setSessionParameters" in android.hardware.camera2.CaptureResult [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ALARM AttributeSdkUsage#USAGE_ALARM" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ASSISTANCE_ACCESSIBILITY AttributeSdkUsage#USAGE_ASSISTANCE_ACCESSIBILITY" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE AttributeSdkUsage#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ASSISTANCE_SONIFICATION AttributeSdkUsage#USAGE_ASSISTANCE_SONIFICATION" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ASSISTANT AttributeSdkUsage#USAGE_ASSISTANT" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_GAME AttributeSdkUsage#USAGE_GAME" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_MEDIA AttributeSdkUsage#USAGE_MEDIA" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_NOTIFICATION_EVENT AttributeSdkUsage#USAGE_NOTIFICATION_EVENT" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_NOTIFICATION_RINGTONE AttributeSdkUsage#USAGE_NOTIFICATION_RINGTONE" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_UNKNOWN AttributeSdkUsage#USAGE_UNKNOWN" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_VOICE_COMMUNICATION AttributeSdkUsage#USAGE_VOICE_COMMUNICATION" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_VOICE_COMMUNICATION_SIGNALLING AttributeSdkUsage#USAGE_VOICE_COMMUNICATION_SIGNALLING" in android.media.AudioAttributes.Builder [101]
-android/media/AudioFormat.java:963: lint: Unresolved link/see tag "android.media.AudioSystem#OUT_CHANNEL_COUNT_MAX AudioSystem#OUT_CHANNEL_COUNT_MAX" in android.media.AudioFormat.Builder [101]
-android/media/AudioManager.java:275: lint: Unresolved link/see tag "android.media.audiopolicy.AudioVolumeGroup" in android.media.AudioManager [101]
-android/media/AudioManager.java:287: lint: Unresolved link/see tag "android.media.audiopolicy.AudioVolumeGroup" in android.media.AudioManager [101]
-android/media/AudioManager.java:311: lint: Unresolved link/see tag "android.media.audiopolicy.AudioVolumeGroup" in android.media.AudioManager [101]
-android/media/AudioManager.java:313: lint: Unresolved link/see tag "android.media.audiopolicy.AudioVolumeGroup" in android.media.AudioManager [101]
-android/media/AudioMetadata.java:118: lint: Unresolved link/see tag "android.media.AudioPresentation.ContentClassifier One of {@link android.media.AudioPresentation#CONTENT_UNKNOWN AudioPresentation#CONTENT_UNKNOWN}, {@link android.media.AudioPresentation#CONTENT_MAIN AudioPresentation#CONTENT_MAIN}, {@link android.media.AudioPresentation#CONTENT_MUSIC_AND_EFFECTS AudioPresentation#CONTENT_MUSIC_AND_EFFECTS}, {@link android.media.AudioPresentation#CONTENT_VISUALLY_IMPAIRED AudioPresentation#CONTENT_VISUALLY_IMPAIRED}, {@link android.media.AudioPresentation#CONTENT_HEARING_IMPAIRED AudioPresentation#CONTENT_HEARING_IMPAIRED}, {@link android.media.AudioPresentation#CONTENT_DIALOG AudioPresentation#CONTENT_DIALOG}, {@link android.media.AudioPresentation#CONTENT_COMMENTARY AudioPresentation#CONTENT_COMMENTARY}, {@link android.media.AudioPresentation#CONTENT_EMERGENCY AudioPresentation#CONTENT_EMERGENCY}, {@link android.media.AudioPresentation#CONTENT_VOICEOVER AudioPresentation#CONTENT_VOICEOVER}." in android.media.AudioMetadata.Format [101]
-android/media/tv/SectionRequest.java:44: lint: Unresolved link/see tag "android.media.tv.BroadcastInfoRequest.RequestOption BroadcastInfoRequest.RequestOption" in android.media.tv.SectionRequest [101]
-android/media/tv/SectionResponse.java:39: lint: Unresolved link/see tag "android.media.tv.BroadcastInfoRequest.RequestOption BroadcastInfoRequest.RequestOption" in android.media.tv.SectionResponse [101]
-android/media/tv/TableRequest.java:48: lint: Unresolved link/see tag "android.media.tv.BroadcastInfoRequest.RequestOption BroadcastInfoRequest.RequestOption" in android.media.tv.TableRequest [101]
-android/media/tv/TableResponse.java:82: lint: Unresolved link/see tag "android.media.tv.BroadcastInfoRequest.RequestOption BroadcastInfoRequest.RequestOption" in android.media.tv.TableResponse [101]
-android/net/EthernetNetworkSpecifier.java:21: lint: Unresolved link/see tag "android.net.EthernetManager" in android.net.EthernetNetworkSpecifier [101]
-android/net/eap/EapSessionConfig.java:120: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.Builder [101]
-android/net/eap/EapSessionConfig.java:135: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.Builder [101]
-android/net/eap/EapSessionConfig.java:148: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.Builder [101]
-android/net/eap/EapSessionConfig.java:161: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.Builder [101]
-android/net/eap/EapSessionConfig.java:288: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.EapAkaConfig [101]
-android/net/eap/EapSessionConfig.java:390: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.EapAkaPrimeConfig [101]
-android/net/eap/EapSessionConfig.java:587: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.EapSimConfig [101]
-android/net/wifi/MloLink.java:32: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.MloLink [101]
-android/net/wifi/MloLink.java:32: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.MloLink [101]
-android/net/wifi/MloLink.java:32: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_6_GHZ WifiScanner#WIFI_BAND_6_GHZ" in android.net.wifi.MloLink [101]
-android/net/wifi/MloLink.java:32: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_UNSPECIFIED WifiScanner#WIFI_BAND_UNSPECIFIED" in android.net.wifi.MloLink [101]
-android/net/wifi/SoftApConfiguration.java:9: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder SoftApConfiguration.Builder" in android.net.wifi.SoftApConfiguration [101]
-android/net/wifi/SoftApConfiguration.java:66: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setSsid(java.lang.String) Builder#setSsid(String)" in android.net.wifi.SoftApConfiguration [101]
-android/net/wifi/SoftApConfiguration.java:85: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setWifiSsid(android.net.wifi.WifiSsid) Builder#setWifiSsid(WifiSsid)" in android.net.wifi.SoftApConfiguration [101]
-android/net/wifi/SoftApConfiguration.java:96: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setBssid(android.net.MacAddress) Builder#setBssid(MacAddress)" in android.net.wifi.SoftApConfiguration [101]
-android/net/wifi/SoftApConfiguration.java:107: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setPassphrase(java.lang.String,int) Builder#setPassphrase(String, int)" in android.net.wifi.SoftApConfiguration [101]
-android/net/wifi/SoftApConfiguration.java:118: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setHiddenSsid(boolean) Builder#setHiddenSsid(boolean)" in android.net.wifi.SoftApConfiguration [101]
-android/net/wifi/WifiManager.java:764: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setBands(int[]) SoftApConfiguration.Builder#setBands(int[])" in android.net.wifi.WifiManager [101]
-android/net/wifi/WifiManager.java:764: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray) SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray)" in android.net.wifi.WifiManager [101]
-android/net/wifi/WifiManager.java:779: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setBands(int[]) SoftApConfiguration.Builder#setBands(int[])" in android.net.wifi.WifiManager [101]
-android/net/wifi/WifiManager.java:779: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray) SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray)" in android.net.wifi.WifiManager [101]
-android/net/wifi/WifiManager.java:2466: lint: Unresolved link/see tag "TelephonyManager#hasCarrierPrivileges()." in android.net.wifi.WifiManager [101]
-android/net/wifi/aware/PublishConfig.java:50: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.aware.PublishConfig [101]
-android/net/wifi/aware/PublishConfig.java:50: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.aware.PublishConfig [101]
-android/net/wifi/aware/PublishConfig.java:249: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.aware.PublishConfig.Builder [101]
-android/net/wifi/aware/PublishConfig.java:249: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.aware.PublishConfig.Builder [101]
-android/net/wifi/aware/SubscribeConfig.java:51: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.aware.SubscribeConfig [101]
-android/net/wifi/aware/SubscribeConfig.java:51: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.aware.SubscribeConfig [101]
-android/net/wifi/aware/SubscribeConfig.java:276: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.aware.SubscribeConfig.Builder [101]
-android/net/wifi/aware/SubscribeConfig.java:276: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.aware.SubscribeConfig.Builder [101]
-android/net/wifi/SoftApConfiguration.java:173: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setShutdownTimeoutMillis(long)" in android.net.wifi.SoftApConfiguration [101]
-android/os/UserManager.java:2384: lint: Unresolved link/see tag "android.annotation.UserHandleAware @UserHandleAware" in android.os.UserManager [101]
+// These are javadoc errors for @ChangeId constants, which are problematic to generate documentation
+// for. They're not necessarily errors in the docs themselves but are also a limitation in the tool.
+// Regardless, the docs currently generated for them is not good, but it is also not used directly
+// in production at the moment.
+// The main limitation is that all references must be fully qualified in order to resolve properly
+// (aside from the normal limitatinos of only being able to @link public APIs).
+// See the CompatInfo.java source file in doclava for more information.
+android/net/wifi/SoftApConfiguration.java:171: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setShutdownTimeoutMillis(long)" in android.net.wifi.SoftApConfiguration [101]
android/os/UserManager.java:2384: lint: Unresolved link/see tag "android.annotation.UserHandleAware#enabledSinceTargetSdkVersion" in android.os.UserManager [101]
-android/service/voice/AlwaysOnHotwordDetector.java:269: lint: Unresolved link/see tag "#initialize( PersistableBundle, SharedMemory, SoundTrigger.ModuleProperties)" in android.service.voice.AlwaysOnHotwordDetector [101]
-android/service/voice/AlwaysOnHotwordDetector.java:269: lint: Unresolved link/see tag "STATE_HARDWARE_UNAVAILABLE" in android.service.voice.AlwaysOnHotwordDetector [101]
-android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "#STATE_ERROR" in android [101]
+android/os/UserManager.java:2384: lint: Unresolved link/see tag "android.annotation.UserHandleAware @UserHandleAware" in android.os.UserManager [101]
+android/service/voice/AlwaysOnHotwordDetector.java:269: lint: Unresolved link/see tag "#initialize( PersistableBundle, SharedMemory, SoundTrigger.ModuleProperties)" in android [101]
+android/service/voice/AlwaysOnHotwordDetector.java:269: lint: Unresolved link/see tag "STATE_HARDWARE_UNAVAILABLE" in android [101]
+android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "android.service.voice.AlwaysOnHotwordDetector.Callback#onFailure" in android [101]
+android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "android.service.voice.AlwaysOnHotwordDetector.Callback#onUnknownFailure" in android [101]
+android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "android.service.voice.AlwaysOnHotwordDetector#STATE_ERROR" in android [101]
android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "Callback#onFailure" in android [101]
android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "Callback#onUnknownFailure" in android [101]
-com/android/internal/policy/PhoneWindow.java:172: lint: Unresolved link/see tag "Build.VERSION_CODES#VANILLA_ICE_CREAM" in com.android.internal.policy.PhoneWindow [101]
-
+android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "#STATE_ERROR" in android [101]
+com/android/internal/policy/PhoneWindow.java:172: lint: Unresolved link/see tag "Build.VERSION_CODES#VANILLA_ICE_CREAM" in android [101]
com/android/server/broadcastradio/aidl/ConversionUtils.java:70: lint: Unresolved link/see tag "IdentifierType#DAB_SID_EXT" in android [101]
com/android/server/broadcastradio/aidl/ConversionUtils.java:70: lint: Unresolved link/see tag "ProgramSelector#IDENTIFIER_TYPE_DAB_DMB_SID_EXT" in android [101]
com/android/server/broadcastradio/aidl/ConversionUtils.java:70: lint: Unresolved link/see tag "RadioTuner" in android [101]
+com/android/server/broadcastradio/aidl/ConversionUtils.java:72: lint: Unresolved link/see tag "com.android.server.broadcastradio.aidl.IdentifierType#DAB_SID_EXT" in android [101]
+com/android/server/broadcastradio/aidl/ConversionUtils.java:72: lint: Unresolved link/see tag "com.android.server.broadcastradio.aidl.ProgramSelector#IDENTIFIER_TYPE_DAB_DMB_SID_EXT" in android [101]
+com/android/server/broadcastradio/aidl/ConversionUtils.java:72: lint: Unresolved link/see tag "com.android.server.broadcastradio.aidl.RadioTuner" in android [101]
+com/android/server/devicepolicy/DevicePolicyManagerService.java:861: lint: Unresolved link/see tag "android.security.IKeyChainService#setGrant" in android [101]
com/android/server/pm/PackageInstallerSession.java:313: lint: Unresolved link/see tag "Build.VERSION_CODES#S API 31" in android [101]
com/android/server/pm/PackageInstallerSession.java:313: lint: Unresolved link/see tag "PackageInstaller.SessionParams#setRequireUserAction" in android [101]
-com/android/server/pm/PackageInstallerSession.java:327: lint: Unresolved link/see tag "#requestUserPreapproval(PreapprovalDetails, IntentSender)" in android [101]
com/android/server/pm/PackageInstallerSession.java:327: lint: Unresolved link/see tag "Build.VERSION_CODES#UPSIDE_DOWN_CAKE API 34" in android [101]
com/android/server/pm/PackageInstallerSession.java:327: lint: Unresolved link/see tag "PackageInstaller.SessionParams#setRequestUpdateOwnership(boolean)" in android [101]
-com/android/server/pm/PackageInstallerSession.java:358: lint: Unresolved link/see tag "IntentSender" in android [101]
-com/android/server/devicepolicy/DevicePolicyManagerService.java:860: lint: Unresolved link/see tag "android.security.IKeyChainService#setGrant" in android [101]
+com/android/server/pm/PackageInstallerSession.java:327: lint: Unresolved link/see tag "#requestUserPreapproval(PreapprovalDetails, IntentSender)" in android [101]
+com/android/server/pm/PackageInstallerSession.java:330: lint: Unresolved link/see tag "com.android.android.server.pm#requestUserPreapproval(PreapprovalDetails, IntentSender)" in android [101]
+com/android/server/pm/PackageInstallerSession.java:358: lint: Unresolved link/see tag "IntentSender" in android [101]
\ No newline at end of file
diff --git a/core/api/current.txt b/core/api/current.txt
index fc1bf36..8c2ba08 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9532,7 +9532,7 @@
method @Nullable public CharSequence getDisplayName();
method public int getId();
method public int getSystemDataSyncFlags();
- method @Nullable public String getTag();
+ method @FlaggedApi("android.companion.association_tag") @Nullable public String getTag();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.companion.AssociationInfo> CREATOR;
}
@@ -9602,7 +9602,7 @@
method @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public void attachSystemDataTransport(int, @NonNull java.io.InputStream, @NonNull java.io.OutputStream) throws android.companion.DeviceNotAssociatedException;
method @Nullable public android.content.IntentSender buildAssociationCancellationIntent();
method @Nullable public android.content.IntentSender buildPermissionTransferUserConsentIntent(int) throws android.companion.DeviceNotAssociatedException;
- method public void clearAssociationTag(int);
+ method @FlaggedApi("android.companion.association_tag") public void clearAssociationTag(int);
method @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public void detachSystemDataTransport(int) throws android.companion.DeviceNotAssociatedException;
method public void disableSystemDataSyncForTypes(int, int);
method @Deprecated public void disassociate(@NonNull String);
@@ -9612,7 +9612,7 @@
method @NonNull public java.util.List<android.companion.AssociationInfo> getMyAssociations();
method @Deprecated public boolean hasNotificationAccess(android.content.ComponentName);
method public void requestNotificationAccess(android.content.ComponentName);
- method public void setAssociationTag(int, @NonNull String);
+ method @FlaggedApi("android.companion.association_tag") public void setAssociationTag(int, @NonNull String);
method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
method public void startSystemDataTransfer(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.companion.CompanionException>) throws android.companion.DeviceNotAssociatedException;
method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void stopObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
@@ -10573,7 +10573,7 @@
public final class ContextParams {
method @Nullable public String getAttributionTag();
method @Nullable public android.content.AttributionSource getNextAttributionSource();
- method @NonNull public boolean shouldRegisterAttributionSource();
+ method @FlaggedApi("android.permission.flags.should_register_attribution_source") @NonNull public boolean shouldRegisterAttributionSource();
}
public static final class ContextParams.Builder {
@@ -10582,7 +10582,7 @@
method @NonNull public android.content.ContextParams build();
method @NonNull public android.content.ContextParams.Builder setAttributionTag(@Nullable String);
method @NonNull public android.content.ContextParams.Builder setNextAttributionSource(@Nullable android.content.AttributionSource);
- method @NonNull public android.content.ContextParams.Builder setShouldRegisterAttributionSource(boolean);
+ method @FlaggedApi("android.permission.flags.should_register_attribution_source") @NonNull public android.content.ContextParams.Builder setShouldRegisterAttributionSource(boolean);
}
public class ContextWrapper extends android.content.Context {
@@ -11721,7 +11721,7 @@
method @NonNull public void setResourceValue(@NonNull String, @IntRange(from=android.util.TypedValue.TYPE_FIRST_INT, to=android.util.TypedValue.TYPE_LAST_INT) int, int, @Nullable String);
method @NonNull public void setResourceValue(@NonNull String, int, @NonNull String, @Nullable String);
method @NonNull public void setResourceValue(@NonNull String, @NonNull android.os.ParcelFileDescriptor, @Nullable String);
- method @NonNull public void setResourceValue(@NonNull String, @NonNull android.content.res.AssetFileDescriptor, @Nullable String);
+ method @FlaggedApi("android.content.res.asset_file_descriptor_frro") @NonNull public void setResourceValue(@NonNull String, @NonNull android.content.res.AssetFileDescriptor, @Nullable String);
method public void setTargetOverlayable(@Nullable String);
}
@@ -12677,6 +12677,7 @@
method public boolean isDeviceUpgrading();
method public abstract boolean isInstantApp();
method public abstract boolean isInstantApp(@NonNull String);
+ method @FlaggedApi("android.content.pm.quarantined_enabled") public boolean isPackageQuarantined(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @FlaggedApi("android.content.pm.stay_stopped") public boolean isPackageStopped(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method public boolean isPackageSuspended(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method public boolean isPackageSuspended();
@@ -12915,6 +12916,7 @@
field public static final int MATCH_DIRECT_BOOT_UNAWARE = 262144; // 0x40000
field public static final int MATCH_DISABLED_COMPONENTS = 512; // 0x200
field public static final int MATCH_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
+ field @FlaggedApi("android.content.pm.quarantined_enabled") public static final long MATCH_QUARANTINED_COMPONENTS = 4294967296L; // 0x100000000L
field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000
field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000
field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
@@ -14354,14 +14356,14 @@
public final class SQLiteDatabase extends android.database.sqlite.SQLiteClosable {
method public void beginTransaction();
method public void beginTransactionNonExclusive();
- method @FlaggedApi("android.database.sqlite.sqlite_apis_15") public void beginTransactionReadOnly();
+ method @FlaggedApi("android.database.sqlite.sqlite_apis_35") public void beginTransactionReadOnly();
method public void beginTransactionWithListener(@Nullable android.database.sqlite.SQLiteTransactionListener);
method public void beginTransactionWithListenerNonExclusive(@Nullable android.database.sqlite.SQLiteTransactionListener);
- method @FlaggedApi("android.database.sqlite.sqlite_apis_15") public void beginTransactionWithListenerReadOnly(@Nullable android.database.sqlite.SQLiteTransactionListener);
+ method @FlaggedApi("android.database.sqlite.sqlite_apis_35") public void beginTransactionWithListenerReadOnly(@Nullable android.database.sqlite.SQLiteTransactionListener);
method public android.database.sqlite.SQLiteStatement compileStatement(String) throws android.database.SQLException;
method @NonNull public static android.database.sqlite.SQLiteDatabase create(@Nullable android.database.sqlite.SQLiteDatabase.CursorFactory);
method @NonNull public static android.database.sqlite.SQLiteDatabase createInMemory(@NonNull android.database.sqlite.SQLiteDatabase.OpenParams);
- method @FlaggedApi("android.database.sqlite.sqlite_apis_15") @NonNull public android.database.sqlite.SQLiteRawStatement createRawStatement(@NonNull String);
+ method @FlaggedApi("android.database.sqlite.sqlite_apis_35") @NonNull public android.database.sqlite.SQLiteRawStatement createRawStatement(@NonNull String);
method public int delete(@NonNull String, @Nullable String, @Nullable String[]);
method public static boolean deleteDatabase(@NonNull java.io.File);
method public void disableWriteAheadLogging();
@@ -14372,13 +14374,13 @@
method public void execSQL(@NonNull String, @NonNull Object[]) throws android.database.SQLException;
method public static String findEditTable(String);
method public java.util.List<android.util.Pair<java.lang.String,java.lang.String>> getAttachedDbs();
- method @FlaggedApi("android.database.sqlite.sqlite_apis_15") public long getLastChangedRowCount();
- method @FlaggedApi("android.database.sqlite.sqlite_apis_15") public long getLastInsertRowId();
+ method @FlaggedApi("android.database.sqlite.sqlite_apis_35") public long getLastChangedRowCount();
+ method @FlaggedApi("android.database.sqlite.sqlite_apis_35") public long getLastInsertRowId();
method public long getMaximumSize();
method public long getPageSize();
method public String getPath();
method @Deprecated public java.util.Map<java.lang.String,java.lang.String> getSyncedTables();
- method @FlaggedApi("android.database.sqlite.sqlite_apis_15") public long getTotalChangedRowCount();
+ method @FlaggedApi("android.database.sqlite.sqlite_apis_35") public long getTotalChangedRowCount();
method public int getVersion();
method public boolean inTransaction();
method public long insert(@NonNull String, @Nullable String, @Nullable android.content.ContentValues);
@@ -14600,7 +14602,7 @@
method public int update(@NonNull android.database.sqlite.SQLiteDatabase, @NonNull android.content.ContentValues, @Nullable String, @Nullable String[]);
}
- @FlaggedApi("android.database.sqlite.sqlite_apis_15") public final class SQLiteRawStatement implements java.io.Closeable {
+ @FlaggedApi("android.database.sqlite.sqlite_apis_35") public final class SQLiteRawStatement implements java.io.Closeable {
method public void bindBlob(int, @NonNull byte[]) throws android.database.sqlite.SQLiteException;
method public void bindBlob(int, @NonNull byte[], int, int) throws android.database.sqlite.SQLiteException;
method public void bindDouble(int, double) throws android.database.sqlite.SQLiteException;
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index b5d3ed7..2af3c34 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -6,7 +6,7 @@
field public static final String CONTROL_AUTOMOTIVE_GNSS = "android.permission.CONTROL_AUTOMOTIVE_GNSS";
field public static final String GET_INTENT_SENDER_INTENT = "android.permission.GET_INTENT_SENDER_INTENT";
field public static final String MAKE_UID_VISIBLE = "android.permission.MAKE_UID_VISIBLE";
- field public static final String USE_COMPANION_TRANSPORTS = "android.permission.USE_COMPANION_TRANSPORTS";
+ field @FlaggedApi("android.companion.flags.companion_transport_apis") public static final String USE_COMPANION_TRANSPORTS = "android.permission.USE_COMPANION_TRANSPORTS";
}
}
@@ -85,21 +85,21 @@
package android.companion {
public final class CompanionDeviceManager {
- method @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void addOnMessageReceivedListener(@NonNull java.util.concurrent.Executor, int, @NonNull android.companion.CompanionDeviceManager.OnMessageReceivedListener);
- method @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void addOnTransportsChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.CompanionDeviceManager.OnTransportsChangedListener);
- method @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void removeOnMessageReceivedListener(int, @NonNull android.companion.CompanionDeviceManager.OnMessageReceivedListener);
- method @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void removeOnTransportsChangedListener(@NonNull android.companion.CompanionDeviceManager.OnTransportsChangedListener);
- method @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void sendMessage(int, @NonNull byte[], @NonNull int[]);
- field public static final int MESSAGE_REQUEST_CONTEXT_SYNC = 1667729539; // 0x63678883
- field public static final int MESSAGE_REQUEST_PERMISSION_RESTORE = 1669491075; // 0x63826983
- field public static final int MESSAGE_REQUEST_REMOTE_AUTHENTICATION = 1669494629; // 0x63827765
+ method @FlaggedApi("android.companion.companion_transport_apis") @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void addOnMessageReceivedListener(@NonNull java.util.concurrent.Executor, int, @NonNull android.companion.CompanionDeviceManager.OnMessageReceivedListener);
+ method @FlaggedApi("android.companion.companion_transport_apis") @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void addOnTransportsChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.CompanionDeviceManager.OnTransportsChangedListener);
+ method @FlaggedApi("android.companion.companion_transport_apis") @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void removeOnMessageReceivedListener(int, @NonNull android.companion.CompanionDeviceManager.OnMessageReceivedListener);
+ method @FlaggedApi("android.companion.companion_transport_apis") @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void removeOnTransportsChangedListener(@NonNull android.companion.CompanionDeviceManager.OnTransportsChangedListener);
+ method @FlaggedApi("android.companion.companion_transport_apis") @RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS) public void sendMessage(int, @NonNull byte[], @NonNull int[]);
+ field @FlaggedApi("android.companion.companion_transport_apis") public static final int MESSAGE_REQUEST_CONTEXT_SYNC = 1667729539; // 0x63678883
+ field @FlaggedApi("android.companion.companion_transport_apis") public static final int MESSAGE_REQUEST_PERMISSION_RESTORE = 1669491075; // 0x63826983
+ field @FlaggedApi("android.companion.companion_transport_apis") public static final int MESSAGE_REQUEST_REMOTE_AUTHENTICATION = 1669494629; // 0x63827765
}
- public static interface CompanionDeviceManager.OnMessageReceivedListener {
+ @FlaggedApi("android.companion.companion_transport_apis") public static interface CompanionDeviceManager.OnMessageReceivedListener {
method public void onMessageReceived(int, @NonNull byte[]);
}
- public static interface CompanionDeviceManager.OnTransportsChangedListener {
+ @FlaggedApi("android.companion.companion_transport_apis") public static interface CompanionDeviceManager.OnTransportsChangedListener {
method public void onTransportsChanged(@NonNull java.util.List<android.companion.AssociationInfo>);
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index c72d09d..70f5e2f 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -312,7 +312,7 @@
field public static final String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
field public static final String RENOUNCE_PERMISSIONS = "android.permission.RENOUNCE_PERMISSIONS";
- field @FlaggedApi("backstage_power.report_usage_stats_permission") public static final String REPORT_USAGE_STATS = "android.permission.REPORT_USAGE_STATS";
+ field @FlaggedApi("android.app.usage.report_usage_stats_permission") public static final String REPORT_USAGE_STATS = "android.permission.REPORT_USAGE_STATS";
field @Deprecated public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
@@ -9538,7 +9538,7 @@
method public int getDeviceType();
method @NonNull public android.os.Bundle getExtras();
method @NonNull public String getModelName();
- method public boolean isBatteryCharging();
+ method @FlaggedApi("com.android.wifi.flags.network_provider_battery_charging_status") public boolean isBatteryCharging();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.sharedconnectivity.app.NetworkProviderInfo> CREATOR;
field public static final int DEVICE_TYPE_AUTO = 5; // 0x5
@@ -9552,7 +9552,7 @@
public static final class NetworkProviderInfo.Builder {
ctor public NetworkProviderInfo.Builder(@NonNull String, @NonNull String);
method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo build();
- method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder setBatteryCharging(boolean);
+ method @FlaggedApi("com.android.wifi.flags.network_provider_battery_charging_status") @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder setBatteryCharging(boolean);
method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder setBatteryPercentage(@IntRange(from=0, to=100) int);
method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder setConnectionStrength(@IntRange(from=0, to=4) int);
method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder setDeviceName(@NonNull String);
@@ -16741,6 +16741,23 @@
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.AntennaPosition> CREATOR;
}
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class NtnSignalStrength implements android.os.Parcelable {
+ ctor @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public NtnSignalStrength(@Nullable android.telephony.satellite.NtnSignalStrength);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int getLevel();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.NtnSignalStrength> CREATOR;
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NTN_SIGNAL_STRENGTH_GOOD = 3; // 0x3
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NTN_SIGNAL_STRENGTH_GREAT = 4; // 0x4
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NTN_SIGNAL_STRENGTH_MODERATE = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NTN_SIGNAL_STRENGTH_NONE = 0; // 0x0
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NTN_SIGNAL_STRENGTH_POOR = 1; // 0x1
+ }
+
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface NtnSignalStrengthCallback {
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrength);
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class PointingInfo implements android.os.Parcelable {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents();
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public float getSatelliteAzimuthDegrees();
@@ -16776,6 +16793,7 @@
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.Set<java.lang.Integer> getSatelliteAttachRestrictionReasonsForCarrier(int);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingSatelliteDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatelliteService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForNtnSignalStrengthChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.NtnSignalStrengthCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
@@ -16786,6 +16804,7 @@
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteProvisioned(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void requestIsSatelliteSupported(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestNtnSignalStrength(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.NtnSignalStrength,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteAttachEnabledForCarrier(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteCapabilities(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.SatelliteCapabilities,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteEnabled(boolean, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
@@ -16794,6 +16813,7 @@
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void setDeviceAlignedWithSatellite(boolean);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void startSatelliteTransmissionUpdates(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void stopSatelliteTransmissionUpdates(@NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrengthCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteDatagram(@NonNull android.telephony.satellite.SatelliteDatagramCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteModemStateChanged(@NonNull android.telephony.satellite.SatelliteStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteProvisionStateChanged(@NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index 4fb7b6b..a501031 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -251,10 +251,6 @@
New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioMixingRule.writeToParcel(android.os.Parcel,int)
UnflaggedApi: android.media.audiopolicy.AudioPolicy#updateMixingRules(java.util.List<android.util.Pair<android.media.audiopolicy.AudioMix,android.media.audiopolicy.AudioMixingRule>>):
New API must be flagged with @FlaggedApi: method android.media.audiopolicy.AudioPolicy.updateMixingRules(java.util.List<android.util.Pair<android.media.audiopolicy.AudioMix,android.media.audiopolicy.AudioMixingRule>>)
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.NetworkProviderInfo#isBatteryCharging():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.isBatteryCharging()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder#setBatteryCharging(boolean):
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder.setBatteryCharging(boolean)
UnflaggedApi: android.nfc.cardemulation.AidGroup#CONTENTS_FILE_DESCRIPTOR:
New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.AidGroup.CONTENTS_FILE_DESCRIPTOR
UnflaggedApi: android.nfc.cardemulation.AidGroup#PARCELABLE_WRITE_RETURN_VALUE:
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index b905287..03a58be 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -541,7 +541,7 @@
field public static final String PERMITTED_INPUT_METHODS_POLICY = "permittedInputMethods";
field public static final String PERSONAL_APPS_SUSPENDED_POLICY = "personalAppsSuspended";
field public static final String SCREEN_CAPTURE_DISABLED_POLICY = "screenCaptureDisabled";
- field public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
+ field @FlaggedApi("android.app.admin.flags.policy_engine_migration_v2_enabled") public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
}
public class DevicePolicyManager {
@@ -851,13 +851,13 @@
method @NonNull public android.companion.AssociationInfo.Builder setRevoked(boolean);
method @NonNull public android.companion.AssociationInfo.Builder setSelfManaged(boolean);
method @NonNull public android.companion.AssociationInfo.Builder setSystemDataSyncFlags(int);
- method @NonNull public android.companion.AssociationInfo.Builder setTag(@Nullable String);
+ method @FlaggedApi("android.companion.association_tag") @NonNull public android.companion.AssociationInfo.Builder setTag(@Nullable String);
method @NonNull public android.companion.AssociationInfo.Builder setTimeApproved(long);
}
public final class CompanionDeviceManager {
method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public void enableSecureTransport(boolean);
- field public static final int MESSAGE_REQUEST_PING = 1669362552; // 0x63807378
+ field @FlaggedApi("android.companion.companion_transport_apis") public static final int MESSAGE_REQUEST_PING = 1669362552; // 0x63807378
}
public abstract class CompanionDeviceService extends android.app.Service {
@@ -2117,7 +2117,7 @@
public class SharedConnectivityManager {
method @Nullable public static android.net.wifi.sharedconnectivity.app.SharedConnectivityManager create(@NonNull android.content.Context, @NonNull String, @NonNull String);
- method @NonNull public android.content.BroadcastReceiver getBroadcastReceiver();
+ method @FlaggedApi("com.android.wifi.flags.shared_connectivity_broadcast_receiver_test_api") @NonNull public android.content.BroadcastReceiver getBroadcastReceiver();
method @Nullable public android.content.ServiceConnection getServiceConnection();
method public void setService(@Nullable android.os.IInterface);
}
@@ -2148,7 +2148,7 @@
}
public final class BugreportParams {
- field public static final int BUGREPORT_MODE_MAX_VALUE = 7; // 0x7
+ field @FlaggedApi("android.os.bugreport_mode_max_value") public static final int BUGREPORT_MODE_MAX_VALUE = 7; // 0x7
}
public class Build {
@@ -3797,14 +3797,14 @@
public final class InputMethodManager {
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void addVirtualStylusIdForTestSession();
method public int getDisplayId();
- method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodInfo> getEnabledInputMethodListAsUser(@NonNull android.os.UserHandle);
- method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser(@NonNull String, boolean, @NonNull android.os.UserHandle);
+ method @FlaggedApi("android.view.inputmethod.imm_userhandle_hostsidetests") @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodInfo> getEnabledInputMethodListAsUser(@NonNull android.os.UserHandle);
+ method @FlaggedApi("android.view.inputmethod.imm_userhandle_hostsidetests") @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser(@NonNull String, boolean, @NonNull android.os.UserHandle);
method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int);
method public boolean hasActiveInputConnection(@Nullable android.view.View);
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean hasPendingImeVisibilityRequests();
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isCurrentRootView(@NonNull android.view.View);
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isInputMethodPickerShown();
- method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public boolean isStylusHandwritingAvailableAsUser(@NonNull android.os.UserHandle);
+ method @FlaggedApi("android.view.inputmethod.imm_userhandle_hostsidetests") @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public boolean isStylusHandwritingAvailableAsUser(@NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void setStylusWindowIdleTimeoutForTest(long);
field public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // 0xcc1a029L
}
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 93e39d5..105e764 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -257,8 +257,6 @@
New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.loadSoundModel(android.hardware.soundtrigger.SoundTrigger.SoundModel)
UnflaggedApi: android.media.soundtrigger.SoundTriggerManager.Model#getSoundModel():
New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.Model.getSoundModel()
-UnflaggedApi: android.net.wifi.sharedconnectivity.app.SharedConnectivityManager#getBroadcastReceiver():
- New API must be flagged with @FlaggedApi: method android.net.wifi.sharedconnectivity.app.SharedConnectivityManager.getBroadcastReceiver()
UnflaggedApi: android.os.BatteryManager#BATTERY_PLUGGED_ANY:
New API must be flagged with @FlaggedApi: field android.os.BatteryManager.BATTERY_PLUGGED_ANY
UnflaggedApi: android.os.BugreportParams#BUGREPORT_MODE_MAX_VALUE:
diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java
index 794495c..369a781 100644
--- a/core/java/android/app/LocaleConfig.java
+++ b/core/java/android/app/LocaleConfig.java
@@ -22,7 +22,6 @@
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
@@ -138,15 +137,14 @@
return;
}
}
- int resId = 0;
Resources res = context.getResources();
+ //Get the resource id
+ int resId = context.getApplicationInfo().getLocaleConfigRes();
+ if (resId == 0) {
+ mStatus = STATUS_NOT_SPECIFIED;
+ return;
+ }
try {
- //Get the resource id
- resId = new ApplicationInfo(context.getApplicationInfo()).getLocaleConfigRes();
- if (resId == 0) {
- mStatus = STATUS_NOT_SPECIFIED;
- return;
- }
//Get the parser to read XML data
XmlResourceParser parser = res.getXml(resId);
parseLocaleConfig(parser, res);
diff --git a/core/java/android/app/admin/DevicePolicyIdentifiers.java b/core/java/android/app/admin/DevicePolicyIdentifiers.java
index ad0af72..84b1ca5 100644
--- a/core/java/android/app/admin/DevicePolicyIdentifiers.java
+++ b/core/java/android/app/admin/DevicePolicyIdentifiers.java
@@ -16,10 +16,13 @@
package android.app.admin;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.TestApi;
+import android.app.admin.flags.Flags;
import android.os.UserManager;
+
import java.util.Objects;
/**
@@ -164,6 +167,7 @@
*
* @hide
*/
+ @FlaggedApi(Flags.FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)
@TestApi
public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
diff --git a/core/java/android/app/admin/SystemUpdatePolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java
index c760298..113a6dd 100644
--- a/core/java/android/app/admin/SystemUpdatePolicy.java
+++ b/core/java/android/app/admin/SystemUpdatePolicy.java
@@ -125,7 +125,7 @@
*
* <p>The system limits each update to one 30-day postponement. The period begins when the
* system first postpones the update and setting new {@code TYPE_POSTPONE} policies won’t extend
- * the period. If, after 30 days the update isn’t installed (through policy changes), the system
+ * the period. If, after 30 days the update isn't installed (through policy changes), the system
* prompts the user to install the update.
*
* <p><strong>Note</strong>: Device manufacturers or carriers might choose to exempt important
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/flags/FlagUtils.java b/core/java/android/app/admin/flags/FlagUtils.java
similarity index 68%
rename from services/devicepolicy/java/com/android/server/devicepolicy/flags/FlagUtils.java
rename to core/java/android/app/admin/flags/FlagUtils.java
index 7e17ef11..7c3c3d5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/flags/FlagUtils.java
+++ b/core/java/android/app/admin/flags/FlagUtils.java
@@ -14,15 +14,20 @@
* limitations under the License.
*/
-package com.android.server.devicepolicy.flags;
+package android.app.admin.flags;
-import static com.android.server.devicepolicy.flags.Flags.devicePolicySizeTrackingEnabled;
-import static com.android.server.devicepolicy.flags.Flags.policyEngineMigrationV2Enabled;
+import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
+import static android.app.admin.flags.Flags.policyEngineMigrationV2Enabled;
+import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
import android.os.Binder;
+/**
+ *
+ * @hide
+ */
public final class FlagUtils {
- private FlagUtils(){}
+ private FlagUtils() {}
public static boolean isPolicyEngineMigrationV2Enabled() {
return Binder.withCleanCallingIdentity(() -> {
@@ -35,4 +40,10 @@
return devicePolicySizeTrackingEnabled();
});
}
+
+ public static boolean isOnboardingBugreportV2Enabled() {
+ return Binder.withCleanCallingIdentity(() -> {
+ return onboardingBugreportV2Enabled();
+ });
+ }
}
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
new file mode 100644
index 0000000..c145c02
--- /dev/null
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -0,0 +1,22 @@
+package: "android.app.admin.flags"
+
+flag {
+ name: "policy_engine_migration_v2_enabled"
+ namespace: "enterprise"
+ description: "V2 of the policy engine migrations for Android V"
+ bug: "289520697"
+}
+
+flag {
+ name: "device_policy_size_tracking_enabled"
+ namespace: "enterprise"
+ description: "Add feature to track the total policy size and have a max threshold."
+ bug: "281543351"
+}
+
+flag {
+ name: "onboarding_bugreport_v2_enabled"
+ namespace: "enterprise"
+ description: "Add feature to track required changes for enabled V2 of auto-capturing of onboarding bug reports."
+ bug: "302517677"
+}
\ No newline at end of file
diff --git a/core/java/android/app/usage/flags.aconfig b/core/java/android/app/usage/flags.aconfig
index d1f9067..f401a76 100644
--- a/core/java/android/app/usage/flags.aconfig
+++ b/core/java/android/app/usage/flags.aconfig
@@ -2,7 +2,7 @@
flag {
name: "user_interaction_type_api"
- namespace: "power_optimization"
+ namespace: "backstage_power"
description: "Feature flag for user interaction event report/query API"
bug: "296061232"
}
diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index 6393c45..8b09bdf 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -144,6 +144,7 @@
* @return the tag of this association.
* @see CompanionDeviceManager#setAssociationTag(int, String)
*/
+ @FlaggedApi(Flags.FLAG_ASSOCIATION_TAG)
@Nullable
public String getTag() {
return mTag;
@@ -459,6 +460,7 @@
}
/** @hide */
+ @FlaggedApi(Flags.FLAG_ASSOCIATION_TAG)
@TestApi
@NonNull
public Builder setTag(@Nullable String tag) {
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index a84845a..70811bb 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -23,6 +23,7 @@
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -216,12 +217,14 @@
*
* @hide
*/
+ @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS)
@TestApi public static final int MESSAGE_REQUEST_PING = 0x63807378; // ?PIN
/**
* Message header assigned to the remote authentication handshakes.
*
* @hide
*/
+ @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS)
@SystemApi(client = MODULE_LIBRARIES)
public static final int MESSAGE_REQUEST_REMOTE_AUTHENTICATION = 0x63827765; // ?RMA
/**
@@ -229,6 +232,7 @@
*
* @hide
*/
+ @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS)
@SystemApi(client = MODULE_LIBRARIES)
public static final int MESSAGE_REQUEST_CONTEXT_SYNC = 0x63678883; // ?CXS
/**
@@ -236,6 +240,7 @@
*
* @hide
*/
+ @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS)
@SystemApi(client = MODULE_LIBRARIES)
public static final int MESSAGE_REQUEST_PERMISSION_RESTORE = 0x63826983; // ?RES
@@ -873,6 +878,7 @@
*
* @hide
*/
+ @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS)
@SystemApi(client = MODULE_LIBRARIES)
public interface OnTransportsChangedListener {
/**
@@ -892,6 +898,7 @@
*
* @hide
*/
+ @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS)
@SystemApi(client = MODULE_LIBRARIES)
@RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS)
public void addOnTransportsChangedListener(
@@ -913,6 +920,7 @@
*
* @hide
*/
+ @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS)
@SystemApi(client = MODULE_LIBRARIES)
@RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS)
public void removeOnTransportsChangedListener(
@@ -934,6 +942,7 @@
*
* @hide
*/
+ @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS)
@SystemApi(client = MODULE_LIBRARIES)
@RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS)
public void sendMessage(int messageType, @NonNull byte[] data, @NonNull int[] associationIds) {
@@ -951,6 +960,7 @@
*
* @hide
*/
+ @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS)
@SystemApi(client = MODULE_LIBRARIES)
public interface OnMessageReceivedListener {
/**
@@ -964,6 +974,7 @@
*
* @hide
*/
+ @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS)
@SystemApi(client = MODULE_LIBRARIES)
@RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS)
public void addOnMessageReceivedListener(
@@ -983,6 +994,7 @@
*
* @hide
*/
+ @FlaggedApi(Flags.FLAG_COMPANION_TRANSPORT_APIS)
@SystemApi(client = MODULE_LIBRARIES)
@RequiresPermission(android.Manifest.permission.USE_COMPANION_TRANSPORTS)
public void removeOnMessageReceivedListener(int messageType,
@@ -1423,6 +1435,7 @@
* of the companion device recorded by CompanionDeviceManager
* @param tag the tag of this association
*/
+ @FlaggedApi(Flags.FLAG_ASSOCIATION_TAG)
@UserHandleAware
public void setAssociationTag(int associationId, @NonNull String tag) {
Objects.requireNonNull(tag, "tag cannot be null");
@@ -1447,6 +1460,7 @@
* of the companion device recorded by CompanionDeviceManager
* @see CompanionDeviceManager#setAssociationTag(int, String)
*/
+ @FlaggedApi(Flags.FLAG_ASSOCIATION_TAG)
@UserHandleAware
public void clearAssociationTag(int associationId) {
try {
diff --git a/core/java/android/companion/flags.aconfig b/core/java/android/companion/flags.aconfig
index b9e5609..4f9c849 100644
--- a/core/java/android/companion/flags.aconfig
+++ b/core/java/android/companion/flags.aconfig
@@ -5,4 +5,18 @@
namespace: "companion"
description: "Controls if the new Builder is exposed to test apis."
bug: "296251481"
+}
+
+flag {
+ name: "companion_transport_apis"
+ namespace: "companion"
+ description: "Grants access to the companion transport apis."
+ bug: "288297505"
+}
+
+flag {
+ name: "association_tag"
+ namespace: "companion"
+ description: "Enable Association tag APIs "
+ bug: "289241123"
}
\ No newline at end of file
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensor.java b/core/java/android/companion/virtual/sensor/VirtualSensor.java
index eaa1792..14c7997 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensor.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensor.java
@@ -115,6 +115,11 @@
parcel.writeStrongBinder(mToken);
}
+ @Override
+ public String toString() {
+ return "VirtualSensor{ mType=" + mType + ", mName='" + mName + "' }";
+ }
+
/**
* Send a sensor event to the system.
*/
diff --git a/core/java/android/content/ContextParams.java b/core/java/android/content/ContextParams.java
index 988a9c0..b844d35 100644
--- a/core/java/android/content/ContextParams.java
+++ b/core/java/android/content/ContextParams.java
@@ -16,7 +16,10 @@
package android.content;
+import static android.permission.flags.Flags.FLAG_SHOULD_REGISTER_ATTRIBUTION_SOURCE;
+
import android.Manifest;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -102,6 +105,7 @@
* registered.
*/
@NonNull
+ @FlaggedApi(FLAG_SHOULD_REGISTER_ATTRIBUTION_SOURCE)
public boolean shouldRegisterAttributionSource() {
return mShouldRegisterAttributionSource;
}
@@ -179,6 +183,7 @@
* created should be registered.
*/
@NonNull
+ @FlaggedApi(FLAG_SHOULD_REGISTER_ATTRIBUTION_SOURCE)
public Builder setShouldRegisterAttributionSource(boolean shouldRegister) {
mShouldRegisterAttributionSource = shouldRegister;
return this;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index b765562..bb9cc0b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -64,6 +64,7 @@
import android.os.ResultReceiver;
import android.os.ShellCommand;
import android.os.StrictMode;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.provider.ContactsContract.QuickContact;
@@ -2796,6 +2797,8 @@
* started and is no longer considered stopped.
* <ul>
* <li> {@link #EXTRA_UID} containing the integer uid assigned to the package.
+ * <li> {@link #EXTRA_TIME} containing the {@link SystemClock#elapsedRealtime()
+ * elapsed realtime} of when the package was unstopped.
* </ul>
*
* <p class="note">This is a protected intent that can only be sent by the system.
@@ -2869,9 +2872,15 @@
*
* <p class="note">This is a protected intent that can only be sent
* by the system.
+ * <p>
+ * Starting in {@link Build.VERSION_CODES#VANILLA_ICE_CREAM Android V}, an extra timestamp
+ * {@link #EXTRA_TIME} is included with this broadcast to indicate the exact time the package
+ * was restarted, in {@link SystemClock#elapsedRealtime() elapsed realtime}.
+ * </p>
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED";
+
/**
* Broadcast Action: The user has cleared the data of a package. This should
* be preceded by {@link #ACTION_PACKAGE_RESTARTED}, after which all of
@@ -6578,8 +6587,8 @@
= "android.intent.extra.SHUTDOWN_USERSPACE_ONLY";
/**
- * Optional extra specifying a time in milliseconds since the Epoch. The value must be
- * non-negative.
+ * Optional extra specifying a time in milliseconds. The timebase depends on the Intent
+ * including this extra. The value must be non-negative.
* <p>
* Type: long
* </p>
diff --git a/core/java/android/content/om/FabricatedOverlay.java b/core/java/android/content/om/FabricatedOverlay.java
index c4547b8..df2d7e7 100644
--- a/core/java/android/content/om/FabricatedOverlay.java
+++ b/core/java/android/content/om/FabricatedOverlay.java
@@ -16,6 +16,7 @@
package android.content.om;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -546,6 +547,7 @@
* @param configuration The string representation of the config this overlay is enabled for
*/
@NonNull
+ @FlaggedApi(android.content.res.Flags.FLAG_ASSET_FILE_DESCRIPTOR_FRRO)
public void setResourceValue(
@NonNull String resourceName,
@NonNull AssetFileDescriptor value,
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 1b60f8e..b37da84 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1256,8 +1256,10 @@
public static final long MATCH_ARCHIVED_PACKAGES = 1L << 32;
/**
- * @hide
+ * Querying flag: always match components of packages in quarantined state.
+ * @see #isPackageQuarantined
*/
+ @FlaggedApi(android.content.pm.Flags.FLAG_QUARANTINED_ENABLED)
public static final long MATCH_QUARANTINED_COMPONENTS = 0x100000000L;
/**
@@ -1478,6 +1480,7 @@
INSTALL_ALLOW_DOWNGRADE,
INSTALL_STAGED,
INSTALL_REQUEST_UPDATE_OWNERSHIP,
+ INSTALL_IGNORE_DEXOPT_PROFILE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface InstallFlags {}
@@ -1710,6 +1713,18 @@
public static final int INSTALL_ARCHIVED = 1 << 27;
/**
+ * If set, all dexopt profiles are ignored by dexopt during the installation, including the
+ * profile in the DM file and the profile embedded in the APK file. If an invalid profile is
+ * provided during installation, no warning will be reported by {@code adb install}.
+ *
+ * This option does not affect later dexopt operations (e.g., background dexopt and manual `pm
+ * compile` invocations).
+ *
+ * @hide
+ */
+ public static final int INSTALL_IGNORE_DEXOPT_PROFILE = 1 << 28;
+
+ /**
* Flag parameter for {@link #installPackage} to force a non-staged update of an APEX. This is
* a development-only feature and should not be used on end user devices.
*
@@ -9902,12 +9917,16 @@
/**
* Query if an app is currently quarantined.
+ * A misbehaving app can be quarantined by e.g. a system of another privileged entity.
+ * Quarantined apps are similar to disabled, but still visible in e.g. Launcher.
+ * Only activities of such apps can still be queried, but not services etc.
+ * Quarantined apps can't be bound to, and won't receive broadcasts.
+ * They can't be resolved, unless {@link #MATCH_QUARANTINED_COMPONENTS} specified.
*
* @return {@code true} if the given package is quarantined, {@code false} otherwise
* @throws NameNotFoundException if the package could not be found.
- *
- * @hide
*/
+ @FlaggedApi(android.content.pm.Flags.FLAG_QUARANTINED_ENABLED)
public boolean isPackageQuarantined(@NonNull String packageName) throws NameNotFoundException {
throw new UnsupportedOperationException("isPackageQuarantined not implemented");
}
diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig
index 0c2c0f4..1b8eb07 100644
--- a/core/java/android/content/res/flags.aconfig
+++ b/core/java/android/content/res/flags.aconfig
@@ -8,3 +8,10 @@
# fixed_read_only or device wont boot because of permission issues accessing flags during boot
is_fixed_read_only: true
}
+
+flag {
+ name: "asset_file_descriptor_frro"
+ namespace: "resource_manager"
+ description: "Feature flag for passing in an AssetFileDescriptor to create an frro"
+ bug: "304478666"
+}
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index b003e75..8a4f678 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -701,7 +701,7 @@
* }
* </pre>
*/
- @FlaggedApi(Flags.FLAG_SQLITE_APIS_15)
+ @FlaggedApi(Flags.FLAG_SQLITE_APIS_35)
public void beginTransactionReadOnly() {
beginTransactionWithListenerReadOnly(null);
}
@@ -785,7 +785,7 @@
* }
* </pre>
*/
- @FlaggedApi(Flags.FLAG_SQLITE_APIS_15)
+ @FlaggedApi(Flags.FLAG_SQLITE_APIS_35)
public void beginTransactionWithListenerReadOnly(
@Nullable SQLiteTransactionListener transactionListener) {
beginTransaction(transactionListener, SQLiteSession.TRANSACTION_MODE_DEFERRED);
@@ -2224,7 +2224,7 @@
* @throws IllegalStateException if a transaction is not in progress.
* @throws SQLiteException if the SQL cannot be compiled.
*/
- @FlaggedApi(Flags.FLAG_SQLITE_APIS_15)
+ @FlaggedApi(Flags.FLAG_SQLITE_APIS_35)
@NonNull
public SQLiteRawStatement createRawStatement(@NonNull String sql) {
Objects.requireNonNull(sql);
@@ -2244,7 +2244,7 @@
* @return The ROWID of the last row to be inserted under this connection.
* @throws IllegalStateException if there is no current transaction.
*/
- @FlaggedApi(Flags.FLAG_SQLITE_APIS_15)
+ @FlaggedApi(Flags.FLAG_SQLITE_APIS_35)
public long getLastInsertRowId() {
return getThreadSession().getLastInsertRowId();
}
@@ -2258,7 +2258,7 @@
* @return The number of rows changed by the most recent sql statement
* @throws IllegalStateException if there is no current transaction.
*/
- @FlaggedApi(Flags.FLAG_SQLITE_APIS_15)
+ @FlaggedApi(Flags.FLAG_SQLITE_APIS_35)
public long getLastChangedRowCount() {
return getThreadSession().getLastChangedRowCount();
}
@@ -2286,7 +2286,7 @@
* @return The number of rows changed on the current connection.
* @throws IllegalStateException if there is no current transaction.
*/
- @FlaggedApi(Flags.FLAG_SQLITE_APIS_15)
+ @FlaggedApi(Flags.FLAG_SQLITE_APIS_35)
public long getTotalChangedRowCount() {
return getThreadSession().getTotalChangedRowCount();
}
diff --git a/core/java/android/database/sqlite/SQLiteRawStatement.java b/core/java/android/database/sqlite/SQLiteRawStatement.java
index 827420f..33f602b 100644
--- a/core/java/android/database/sqlite/SQLiteRawStatement.java
+++ b/core/java/android/database/sqlite/SQLiteRawStatement.java
@@ -71,7 +71,7 @@
*
* @see <a href="http://sqlite.org/c3ref/stmt.html">sqlite3_stmt</a>
*/
-@FlaggedApi(Flags.FLAG_SQLITE_APIS_15)
+@FlaggedApi(Flags.FLAG_SQLITE_APIS_35)
public final class SQLiteRawStatement implements Closeable {
private static final String TAG = "SQLiteRawStatement";
diff --git a/core/java/android/database/sqlite/flags.aconfig b/core/java/android/database/sqlite/flags.aconfig
index 564df03..62a5123 100644
--- a/core/java/android/database/sqlite/flags.aconfig
+++ b/core/java/android/database/sqlite/flags.aconfig
@@ -1,7 +1,7 @@
package: "android.database.sqlite"
flag {
- name: "sqlite_apis_15"
+ name: "sqlite_apis_35"
namespace: "system_performance"
is_fixed_read_only: true
description: "SQLite APIs held back for Android 15"
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index f033f97..bcf447b 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -16,6 +16,7 @@
package android.hardware;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.SystemService;
@@ -27,6 +28,8 @@
import android.util.Log;
import android.util.SparseArray;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -1809,6 +1812,41 @@
protected abstract boolean cancelTriggerSensorImpl(TriggerEventListener listener,
Sensor sensor, boolean disable);
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({DATA_INJECTION, REPLAY_DATA_INJECTION, HAL_BYPASS_REPLAY_DATA_INJECTION})
+ public @interface DataInjectionMode {}
+ /**
+ * This mode is only used for testing purposes. Not all HALs support this mode. In this mode,
+ * the HAL ignores the sensor data provided by physical sensors and accepts the data that is
+ * injected from the SensorService as if it were the real sensor data. This mode is primarily
+ * used for testing various algorithms like vendor provided SensorFusion, Step Counter and
+ * Step Detector etc. Typically, in this mode, there is a client app which injects
+ * sensor data into the HAL. Normal apps can register and unregister for any sensor
+ * that supports injection. Registering to sensors that do not support injection will
+ * give an error.
+ * This is the default data injection mode.
+ * @hide
+ */
+ public static final int DATA_INJECTION = 1;
+ /**
+ * Mostly equivalent to DATA_INJECTION with the difference being that the injected data is
+ * delivered to all requesting apps rather than just the package allowed to inject data.
+ * This mode is only allowed to be used on development builds.
+ * @hide
+ */
+ public static final int REPLAY_DATA_INJECTION = 3;
+ /**
+ * Like REPLAY_DATA_INJECTION but injected data is not sent into the HAL. It is stored in a
+ * buffer in the platform and played back to all requesting apps.
+ * This is useful for playing back sensor data to test platform components without
+ * relying on the HAL to support data injection.
+ * @hide
+ */
+ public static final int HAL_BYPASS_REPLAY_DATA_INJECTION = 4;
+
/**
* For testing purposes only. Not for third party applications.
@@ -1833,13 +1871,47 @@
*/
@SystemApi
public boolean initDataInjection(boolean enable) {
- return initDataInjectionImpl(enable);
+ return initDataInjectionImpl(enable, DATA_INJECTION);
+ }
+
+ /**
+ * For testing purposes only. Not for third party applications.
+ *
+ * Initialize data injection mode and create a client for data injection. SensorService should
+ * already be operating in one of DATA_INJECTION, REPLAY_DATA_INJECTION or
+ * HAL_BYPASS_REPLAY_DATA_INJECTION modes for this call succeed. To set SensorService in
+ * a Data Injection mode, use one of:
+ *
+ * <ul>
+ * <li>adb shell dumpsys sensorservice data_injection</li>
+ * <li>adb shell dumpsys sensorservice replay_data_injection package_name</li>
+ * <li>adb shell dumpsys sensorservice hal_bypass_replay_data_injection package_name</li>
+ * </ul>
+ *
+ * Typically this is done using a host side test. This mode is expected to be used
+ * only for testing purposes. See {@link DataInjectionMode} for details of each data injection
+ * mode. Once this method succeeds, the test can call
+ * {@link #injectSensorData(Sensor, float[], int, long)} to inject sensor data into the HAL.
+ * To put SensorService back into normal mode, use "adb shell dumpsys sensorservice enable"
+ *
+ * @param enable True to initialize a client in a data injection mode.
+ * False to clean up the native resources.
+ *
+ * @param mode One of DATA_INJECTION, REPLAY_DATA_INJECTION or HAL_BYPASS_DATA_INJECTION.
+ * See {@link DataInjectionMode} for details.
+ *
+ * @return true if the HAL supports data injection and false
+ * otherwise.
+ * @hide
+ */
+ public boolean initDataInjection(boolean enable, @DataInjectionMode int mode) {
+ return initDataInjectionImpl(enable, mode);
}
/**
* @hide
*/
- protected abstract boolean initDataInjectionImpl(boolean enable);
+ protected abstract boolean initDataInjectionImpl(boolean enable, @DataInjectionMode int mode);
/**
* For testing purposes only. Not for third party applications.
@@ -1871,9 +1943,6 @@
if (sensor == null) {
throw new IllegalArgumentException("sensor cannot be null");
}
- if (!sensor.isDataInjectionSupported()) {
- throw new IllegalArgumentException("sensor does not support data injection");
- }
if (values == null) {
throw new IllegalArgumentException("sensor data cannot be null");
}
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index dfd3802..40e03db 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -90,6 +90,8 @@
private static native void nativeGetRuntimeSensors(
long nativeInstance, int deviceId, List<Sensor> list);
private static native boolean nativeIsDataInjectionEnabled(long nativeInstance);
+ private static native boolean nativeIsReplayDataInjectionEnabled(long nativeInstance);
+ private static native boolean nativeIsHalBypassReplayDataInjectionEnabled(long nativeInstance);
private static native int nativeCreateDirectChannel(
long nativeInstance, int deviceId, long size, int channelType, int fd,
@@ -384,20 +386,41 @@
}
}
- protected boolean initDataInjectionImpl(boolean enable) {
+ protected boolean initDataInjectionImpl(boolean enable, @DataInjectionMode int mode) {
synchronized (sLock) {
+ boolean isDataInjectionModeEnabled = false;
if (enable) {
- boolean isDataInjectionModeEnabled = nativeIsDataInjectionEnabled(mNativeInstance);
+ switch (mode) {
+ case DATA_INJECTION:
+ isDataInjectionModeEnabled = nativeIsDataInjectionEnabled(mNativeInstance);
+ break;
+ case REPLAY_DATA_INJECTION:
+ isDataInjectionModeEnabled = nativeIsReplayDataInjectionEnabled(
+ mNativeInstance);
+ break;
+ case HAL_BYPASS_REPLAY_DATA_INJECTION:
+ isDataInjectionModeEnabled = nativeIsHalBypassReplayDataInjectionEnabled(
+ mNativeInstance);
+ break;
+ default:
+ break;
+ }
// The HAL does not support injection OR SensorService hasn't been set in DI mode.
if (!isDataInjectionModeEnabled) {
- Log.e(TAG, "Data Injection mode not enabled");
+ Log.e(TAG, "The correct Data Injection mode has not been enabled");
return false;
}
+ if (sInjectEventQueue != null && sInjectEventQueue.getDataInjectionMode() != mode) {
+ // The inject event queue has been initialized for a different type of DI
+ // close it and create a new one
+ sInjectEventQueue.dispose();
+ sInjectEventQueue = null;
+ }
// Initialize a client for data_injection.
if (sInjectEventQueue == null) {
try {
sInjectEventQueue = new InjectEventQueue(
- mMainLooper, this, mContext.getPackageName());
+ mMainLooper, this, mode, mContext.getPackageName());
} catch (RuntimeException e) {
Log.e(TAG, "Cannot create InjectEventQueue: " + e);
}
@@ -421,6 +444,12 @@
Log.e(TAG, "Data injection mode not activated before calling injectSensorData");
return false;
}
+ if (sInjectEventQueue.getDataInjectionMode() != HAL_BYPASS_REPLAY_DATA_INJECTION
+ && !sensor.isDataInjectionSupported()) {
+ // DI mode and Replay DI mode require support from the sensor HAL
+ // HAL Bypass mode doesn't require this.
+ throw new IllegalArgumentException("sensor does not support data injection");
+ }
int ret = sInjectEventQueue.injectSensorData(sensor.getHandle(), values, accuracy,
timestamp);
// If there are any errors in data injection clean up the native resources.
@@ -825,6 +854,8 @@
protected static final int OPERATING_MODE_NORMAL = 0;
protected static final int OPERATING_MODE_DATA_INJECTION = 1;
+ protected static final int OPERATING_MODE_REPLAY_DATA_INJECTION = 3;
+ protected static final int OPERATING_MODE_HAL_BYPASS_REPLAY_DATA_INJECTION = 4;
BaseEventQueue(Looper looper, SystemSensorManager manager, int mode, String packageName) {
if (packageName == null) packageName = "";
@@ -1134,8 +1165,12 @@
}
final class InjectEventQueue extends BaseEventQueue {
- public InjectEventQueue(Looper looper, SystemSensorManager manager, String packageName) {
- super(looper, manager, OPERATING_MODE_DATA_INJECTION, packageName);
+
+ private int mMode;
+ public InjectEventQueue(Looper looper, SystemSensorManager manager,
+ @DataInjectionMode int mode, String packageName) {
+ super(looper, manager, mode, packageName);
+ mMode = mode;
}
int injectSensorData(int handle, float[] values, int accuracy, long timestamp) {
@@ -1161,6 +1196,10 @@
protected void removeSensorEvent(Sensor sensor) {
}
+
+ int getDataInjectionMode() {
+ return mMode;
+ }
}
protected boolean setOperationParameterImpl(SensorAdditionalInfo parameter) {
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 8decd50..2b5f5ee 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -220,7 +220,7 @@
registerCallbackIfNeededLocked();
- if (DEBUG || extraLogging()) {
+ if (DEBUG) {
Log.d(TAG, "getDisplayInfo: displayId=" + displayId + ", info=" + info);
}
return info;
@@ -402,7 +402,7 @@
}
private void maybeLogAllDisplayListeners() {
- if (!sExtraDisplayListenerLogging) {
+ if (!extraLogging()) {
return;
}
@@ -1222,7 +1222,7 @@
private void handleMessage(Message msg) {
if (extraLogging()) {
- Slog.i(TAG, "DisplayListenerDelegate(" + eventToString(msg.what)
+ Slog.i(TAG, "DLD(" + eventToString(msg.what)
+ ", display=" + msg.arg1
+ ", mEventsMask=" + Long.toBinaryString(mEventsMask)
+ ", mPackageName=" + mPackageName
@@ -1231,9 +1231,10 @@
}
if (DEBUG) {
Trace.beginSection(
- "DisplayListenerDelegate(" + eventToString(msg.what)
+ TextUtils.trimToSize(
+ "DLD(" + eventToString(msg.what)
+ ", display=" + msg.arg1
- + ", listener=" + mListener.getClass() + ")");
+ + ", listener=" + mListener.getClass() + ")", 127));
}
switch (msg.what) {
case EVENT_DISPLAY_ADDED:
@@ -1422,11 +1423,12 @@
sExtraDisplayListenerLogging = !TextUtils.isEmpty(EXTRA_LOGGING_PACKAGE_NAME)
&& EXTRA_LOGGING_PACKAGE_NAME.equals(sCurrentPackageName);
}
- return sExtraDisplayListenerLogging;
+ // TODO: b/306170135 - return sExtraDisplayListenerLogging instead
+ return true;
}
private static boolean extraLogging() {
- return sExtraDisplayListenerLogging && EXTRA_LOGGING_PACKAGE_NAME.equals(
- sCurrentPackageName);
+ // TODO: b/306170135 - return sExtraDisplayListenerLogging & package name check instead
+ return true;
}
}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 0221296..02304b5 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -1074,6 +1074,14 @@
*/
public interface FaceDetectionCallback {
void onFaceDetected(int sensorId, int userId, boolean isStrongBiometric);
+
+ /**
+ * An error has occurred with face detection.
+ *
+ * This callback signifies that this operation has been completed, and
+ * no more callbacks should be expected.
+ */
+ default void onDetectionError(int errorMsgId) {}
}
/**
@@ -1373,6 +1381,9 @@
} else if (mRemovalCallback != null) {
mRemovalCallback.onRemovalError(mRemovalFace, clientErrMsgId,
getErrorString(mContext, errMsgId, vendorCode));
+ } else if (mFaceDetectionCallback != null) {
+ mFaceDetectionCallback.onDetectionError(errMsgId);
+ mFaceDetectionCallback = null;
}
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 5bfda70..935157a 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -462,6 +462,14 @@
* Invoked when a fingerprint has been detected.
*/
void onFingerprintDetected(int sensorId, int userId, boolean isStrongBiometric);
+
+ /**
+ * An error has occurred with fingerprint detection.
+ *
+ * This callback signifies that this operation has been completed, and
+ * no more callbacks should be expected.
+ */
+ default void onDetectionError(int errorMsgId) {}
}
/**
@@ -1484,6 +1492,9 @@
? mRemoveTracker.mSingleFingerprint : null;
mRemovalCallback.onRemovalError(fp, clientErrMsgId,
getErrorString(mContext, errMsgId, vendorCode));
+ } else if (mFingerprintDetectionCallback != null) {
+ mFingerprintDetectionCallback.onDetectionError(errMsgId);
+ mFingerprintDetectionCallback = null;
}
}
diff --git a/core/java/android/hardware/input/InputDeviceSensorManager.java b/core/java/android/hardware/input/InputDeviceSensorManager.java
index aa55e54..05024ea 100644
--- a/core/java/android/hardware/input/InputDeviceSensorManager.java
+++ b/core/java/android/hardware/input/InputDeviceSensorManager.java
@@ -644,7 +644,7 @@
}
@Override
- protected boolean initDataInjectionImpl(boolean enable) {
+ protected boolean initDataInjectionImpl(boolean enable, int mode) {
return false;
}
diff --git a/core/java/android/os/BugreportParams.java b/core/java/android/os/BugreportParams.java
index 47ad72f..e8ad303 100644
--- a/core/java/android/os/BugreportParams.java
+++ b/core/java/android/os/BugreportParams.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -133,6 +134,7 @@
* The maximum value of supported bugreport mode.
* @hide
*/
+ @FlaggedApi(android.os.Flags.FLAG_BUGREPORT_MODE_MAX_VALUE)
@TestApi
public static final int BUGREPORT_MODE_MAX_VALUE = BUGREPORT_MODE_ONBOARDING;
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index 98f9dff..5078dc35 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -140,6 +140,31 @@
*/
public static final int USAGE_MEDIA = 0x10 | USAGE_CLASS_MEDIA;
+ /** @hide */
+ @IntDef(prefix = { "CATEGORY_" }, value = {
+ CATEGORY_UNKNOWN,
+ CATEGORY_KEYBOARD,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Category {}
+
+ /**
+ * Category value when the vibration category is unknown.
+ *
+ * @hide
+ */
+ public static final int CATEGORY_UNKNOWN = 0x0;
+
+ /**
+ * Category value for keyboard vibrations.
+ *
+ * <p>Most typical keyboard vibrations are haptic feedback for virtual keyboard key
+ * press/release, for example.
+ *
+ * @hide
+ */
+ public static final int CATEGORY_KEYBOARD = 1;
+
/**
* @hide
*/
@@ -147,7 +172,8 @@
FLAG_BYPASS_INTERRUPTION_POLICY,
FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF,
FLAG_INVALIDATE_SETTINGS_CACHE,
- FLAG_PIPELINED_EFFECT
+ FLAG_PIPELINED_EFFECT,
+ FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE
})
@Retention(RetentionPolicy.SOURCE)
public @interface Flag{}
@@ -167,6 +193,8 @@
* {@link android.view.HapticFeedbackConstants#FLAG_IGNORE_GLOBAL_SETTING} and
* {@link AudioAttributes#FLAG_BYPASS_MUTE}.
*
+ * <p>Only privileged apps can ignore user settings, and this flag will be ignored otherwise.
+ *
* @hide
*/
public static final int FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF = 1 << 1;
@@ -199,12 +227,31 @@
public static final int FLAG_PIPELINED_EFFECT = 1 << 3;
/**
+ * Flag requesting that this vibration effect to be played without applying the user
+ * intensity setting to scale the vibration.
+ *
+ * <p>The user setting is still applied to enable/disable the vibration, but the vibration
+ * effect strength will not be scaled based on the enabled setting value.
+ *
+ * <p>This is intended to be used on scenarios where the system needs to enforce a specific
+ * strength for the vibration effect, regardless of the user preference. Only privileged apps
+ * can ignore user settings, and this flag will be ignored otherwise.
+ *
+ * <p>If you need to bypass the user setting when it's disabling vibrations then this also
+ * needs the flag {@link #FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF} to be set.
+ *
+ * @hide
+ */
+ public static final int FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE = 1 << 4;
+
+ /**
* All flags supported by vibrator service, update it when adding new flag.
* @hide
*/
public static final int FLAG_ALL_SUPPORTED =
FLAG_BYPASS_INTERRUPTION_POLICY | FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF
- | FLAG_INVALIDATE_SETTINGS_CACHE | FLAG_PIPELINED_EFFECT;
+ | FLAG_INVALIDATE_SETTINGS_CACHE | FLAG_PIPELINED_EFFECT
+ | FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE;
/** Creates a new {@link VibrationAttributes} instance with given usage. */
public static @NonNull VibrationAttributes createForUsage(@Usage int usage) {
@@ -214,12 +261,14 @@
private final int mUsage;
private final int mFlags;
private final int mOriginalAudioUsage;
+ private final int mCategory;
private VibrationAttributes(@Usage int usage, @AudioAttributes.AttributeUsage int audioUsage,
- @Flag int flags) {
+ @Flag int flags, @Category int category) {
mUsage = usage;
mOriginalAudioUsage = audioUsage;
mFlags = flags & FLAG_ALL_SUPPORTED;
+ mCategory = category;
}
/**
@@ -248,6 +297,20 @@
}
/**
+ * Return the vibration category.
+ *
+ * <p>Vibration categories describe the source of the vibration, and it can be combined with
+ * the vibration usage to best match to a user setting, e.g. a vibration with usage touch and
+ * category keyboard can be used to control keyboard haptic feedback independently.
+ *
+ * @hide
+ */
+ @Category
+ public int getCategory() {
+ return mCategory;
+ }
+
+ /**
* Check whether a flag is set
* @return true if a flag is set and false otherwise
*/
@@ -298,12 +361,14 @@
dest.writeInt(mUsage);
dest.writeInt(mOriginalAudioUsage);
dest.writeInt(mFlags);
+ dest.writeInt(mCategory);
}
private VibrationAttributes(Parcel src) {
mUsage = src.readInt();
mOriginalAudioUsage = src.readInt();
mFlags = src.readInt();
+ mCategory = src.readInt();
}
public static final @NonNull Parcelable.Creator<VibrationAttributes>
@@ -326,12 +391,12 @@
}
VibrationAttributes rhs = (VibrationAttributes) o;
return mUsage == rhs.mUsage && mOriginalAudioUsage == rhs.mOriginalAudioUsage
- && mFlags == rhs.mFlags;
+ && mFlags == rhs.mFlags && mCategory == rhs.mCategory;
}
@Override
public int hashCode() {
- return Objects.hash(mUsage, mOriginalAudioUsage, mFlags);
+ return Objects.hash(mUsage, mOriginalAudioUsage, mFlags, mCategory);
}
@Override
@@ -340,6 +405,7 @@
+ "mUsage=" + usageToString()
+ ", mAudioUsage= " + AudioAttributes.usageToString(mOriginalAudioUsage)
+ ", mFlags=" + mFlags
+ + ", mCategory=" + categoryToString()
+ '}';
}
@@ -376,6 +442,23 @@
}
}
+ /** @hide */
+ public String categoryToString() {
+ return categoryToString(mCategory);
+ }
+
+ /** @hide */
+ public static String categoryToString(@Category int category) {
+ switch (category) {
+ case CATEGORY_UNKNOWN:
+ return "UNKNOWN";
+ case CATEGORY_KEYBOARD:
+ return "KEYBOARD";
+ default:
+ return "unknown category " + category;
+ }
+ }
+
/**
* Builder class for {@link VibrationAttributes} objects.
* By default, all information is set to UNKNOWN.
@@ -384,6 +467,7 @@
private int mUsage = USAGE_UNKNOWN;
private int mOriginalAudioUsage = AudioAttributes.USAGE_UNKNOWN;
private int mFlags = 0x0;
+ private int mCategory = CATEGORY_UNKNOWN;
/**
* Constructs a new Builder with the defaults.
@@ -399,6 +483,7 @@
mUsage = vib.mUsage;
mOriginalAudioUsage = vib.mOriginalAudioUsage;
mFlags = vib.mFlags;
+ mCategory = vib.mCategory;
}
}
@@ -464,7 +549,8 @@
* @return a new {@link VibrationAttributes} object
*/
public @NonNull VibrationAttributes build() {
- VibrationAttributes ans = new VibrationAttributes(mUsage, mOriginalAudioUsage, mFlags);
+ VibrationAttributes ans = new VibrationAttributes(
+ mUsage, mOriginalAudioUsage, mFlags, mCategory);
return ans;
}
@@ -480,6 +566,19 @@
}
/**
+ * Sets the attribute describing the category of the corresponding vibration.
+ *
+ * @param category The category for the vibration
+ * @return the same Builder instance.
+ *
+ * @hide
+ */
+ public @NonNull Builder setCategory(@Category int category) {
+ mCategory = category;
+ return this;
+ }
+
+ /**
* Sets only the flags specified in the bitmask, leaving the other supported flag values
* unchanged in the builder.
*
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 99c9925..2fc2414 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -184,6 +184,16 @@
}
/**
+ * Whether the keyboard vibration is enabled by default.
+ *
+ * @return {@code true} if the keyboard vibration is default enabled, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isDefaultKeyboardVibrationEnabled() {
+ return getConfig().isDefaultKeyboardVibrationEnabled();
+ }
+
+ /**
* Return the ID of this vibrator.
*
* @return A non-negative integer representing the id of the vibrator controlled by this
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 7fceda4..c4521c0 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -16,7 +16,7 @@
flag {
name: "remove_app_profiler_pss_collection"
- namespace: "power_optimization"
+ namespace: "backstage_power"
description: "Replaces background PSS collection in AppProfiler with RSS"
bug: "297542292"
}
@@ -27,3 +27,10 @@
description: "Guards a new Private Profile type in UserManager - everything from its setup to config to deletion."
bug: "299069460"
}
+
+flag {
+ name: "bugreport_mode_max_value"
+ namespace: "telephony"
+ description: "Introduce a constant as maximum value of bugreport mode."
+ bug: "305067125"
+}
diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java
index bde334a..92e4967 100644
--- a/core/java/android/os/vibrator/VibrationConfig.java
+++ b/core/java/android/os/vibrator/VibrationConfig.java
@@ -65,6 +65,8 @@
@VibrationIntensity
private final int mDefaultRingVibrationIntensity;
+ private final boolean mDefaultKeyboardVibrationEnabled;
+
/** @hide */
public VibrationConfig(@Nullable Resources resources) {
mHapticChannelMaxVibrationAmplitude = loadFloat(resources,
@@ -76,6 +78,8 @@
mIgnoreVibrationsOnWirelessCharger = loadBoolean(resources,
com.android.internal.R.bool.config_ignoreVibrationsOnWirelessCharger, false);
+ mDefaultKeyboardVibrationEnabled = loadBoolean(resources,
+ com.android.internal.R.bool.config_defaultKeyboardVibrationEnabled, true);
mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources,
com.android.internal.R.integer.config_defaultAlarmVibrationIntensity);
@@ -157,6 +161,14 @@
return mIgnoreVibrationsOnWirelessCharger;
}
+ /**
+ * Whether keyboard vibration settings is enabled by default.
+ * @hide
+ */
+ public boolean isDefaultKeyboardVibrationEnabled() {
+ return mDefaultKeyboardVibrationEnabled;
+ }
+
/** Get the default vibration intensity for given usage. */
@VibrationIntensity
public int getDefaultVibrationIntensity(@VibrationAttributes.Usage int usage) {
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index 66ad12c..69d86a6 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -37,3 +37,10 @@
is_fixed_read_only: true
bug: "291128479"
}
+
+flag {
+ namespace: "haptics"
+ name: "keyboard_category_enabled"
+ description: "Enables the independent keyboard vibration settings feature"
+ bug: "289107579"
+}
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index d60d4c6..3f06a91 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -28,3 +28,10 @@
description: "enable AttributionSource.setNextAttributionSource"
bug: "304478648"
}
+
+flag {
+ name: "should_register_attribution_source"
+ namespace: "permissions"
+ description: "enable the shouldRegisterAttributionSource API"
+ bug: "305057691"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b34e09f..f0906b1 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5129,6 +5129,14 @@
"hardware_haptic_feedback_intensity";
/**
+ * Whether keyboard vibration feedback is enabled. The value is boolean (1 or 0).
+ *
+ * @hide
+ */
+ @Readable
+ public static final String KEYBOARD_VIBRATION_ENABLED = "keyboard_vibration_enabled";
+
+ /**
* Ringer volume. This is used internally, changing this value will not
* change the volume. See AudioManager.
*
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index c82a4ca..2a4cbaf 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -28,8 +28,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
-
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
@@ -59,8 +57,7 @@
* @hide
*/
public NotificationRankingUpdate(Parcel in) {
- if (SystemUiSystemPropertiesFlags.getResolver().isEnabled(
- SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) {
+ if (Flags.rankingUpdateAshmem()) {
// Recover the ranking map from the SharedMemory and store it in mapParcel.
final Parcel mapParcel = Parcel.obtain();
ByteBuffer buffer = null;
@@ -176,8 +173,7 @@
*/
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
- if (SystemUiSystemPropertiesFlags.getResolver().isEnabled(
- SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) {
+ if (Flags.rankingUpdateAshmem()) {
final Parcel mapParcel = Parcel.obtain();
ArrayList<NotificationListenerService.Ranking> marshalableRankings = new ArrayList<>();
Bundle smartActionsBundle = new Bundle();
diff --git a/core/java/android/service/notification/flags.aconfig b/core/java/android/service/notification/flags.aconfig
new file mode 100644
index 0000000..2931435
--- /dev/null
+++ b/core/java/android/service/notification/flags.aconfig
@@ -0,0 +1,9 @@
+package: "android.service.notification"
+
+flag {
+ name: "ranking_update_ashmem"
+ namespace: "systemui"
+ description: "This flag controls moving ranking update contents into ashmem"
+ bug: "284297289"
+}
+
diff --git a/core/java/android/service/rotationresolver/OWNERS b/core/java/android/service/rotationresolver/OWNERS
index 5b57fc7..dce874d 100644
--- a/core/java/android/service/rotationresolver/OWNERS
+++ b/core/java/android/service/rotationresolver/OWNERS
@@ -1,9 +1,7 @@
# Bug component: 814982
asalo@google.com
-augale@google.com
eejiang@google.com
payamp@google.com
siddikap@google.com
-svetoslavganov@google.com
tgadh@google.com
diff --git a/core/java/android/service/voice/AbstractDetector.java b/core/java/android/service/voice/AbstractDetector.java
index 7af7fe6..db97d4f 100644
--- a/core/java/android/service/voice/AbstractDetector.java
+++ b/core/java/android/service/voice/AbstractDetector.java
@@ -199,8 +199,12 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+ Consumer<AbstractDetector> onDestroyListener;
synchronized (mLock) {
- mOnDestroyListener.accept(this);
+ onDestroyListener = mOnDestroyListener;
+ }
+ if (onDestroyListener != null) {
+ onDestroyListener.accept(this);
}
}
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index 3ed13bb..35834fd 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -767,7 +767,7 @@
try {
mService.startListening(recognizerIntent, mListener, mContext.getAttributionSource());
if (DBG) Log.d(TAG, "service start listening command succeeded");
- } catch (final RemoteException e) {
+ } catch (final Exception e) {
Log.e(TAG, "startListening() failed", e);
mListener.onError(ERROR_CLIENT);
}
@@ -781,7 +781,7 @@
try {
mService.stopListening(mListener);
if (DBG) Log.d(TAG, "service stop listening command succeeded");
- } catch (final RemoteException e) {
+ } catch (final Exception e) {
Log.e(TAG, "stopListening() failed", e);
mListener.onError(ERROR_CLIENT);
}
@@ -795,7 +795,7 @@
try {
mService.cancel(mListener, /*isShutdown*/ false);
if (DBG) Log.d(TAG, "service cancel command succeeded");
- } catch (final RemoteException e) {
+ } catch (final Exception e) {
Log.e(TAG, "cancel() failed", e);
mListener.onError(ERROR_CLIENT);
}
@@ -830,7 +830,7 @@
mContext.getAttributionSource(),
new InternalSupportCallback(callbackExecutor, recognitionSupportCallback));
if (DBG) Log.d(TAG, "service support command succeeded");
- } catch (final RemoteException e) {
+ } catch (final Exception e) {
Log.e(TAG, "checkRecognitionSupport() failed", e);
callbackExecutor.execute(() -> recognitionSupportCallback.onError(ERROR_CLIENT));
}
@@ -850,7 +850,7 @@
mService.triggerModelDownload(
recognizerIntent, mContext.getAttributionSource(), null);
if (DBG) Log.d(TAG, "triggerModelDownload() without a listener");
- } catch (final RemoteException e) {
+ } catch (final Exception e) {
Log.e(TAG, "triggerModelDownload() without a listener failed", e);
mListener.onError(ERROR_CLIENT);
}
@@ -862,7 +862,7 @@
recognizerIntent, mContext.getAttributionSource(),
new InternalModelDownloadListener(callbackExecutor, modelDownloadListener));
if (DBG) Log.d(TAG, "triggerModelDownload() with a listener");
- } catch (final RemoteException e) {
+ } catch (final Exception e) {
Log.e(TAG, "triggerModelDownload() with a listener failed", e);
callbackExecutor.execute(() -> modelDownloadListener.onError(ERROR_CLIENT));
}
@@ -889,7 +889,7 @@
if (mService != null) {
try {
mService.cancel(mListener, /*isShutdown*/ true);
- } catch (final RemoteException e) {
+ } catch (final Exception e) {
// Not important
}
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 2906d86..766e924 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -200,6 +200,12 @@
public static final String SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION =
"settings_remote_device_credential_validation";
+ /**
+ * Flag to enable/disable to start treating any calls to "suspend" an app as "quarantine".
+ * @hide
+ */
+ public static final String SETTINGS_TREAT_PAUSE_AS_QUARANTINE =
+ "settings_treat_pause_as_quarantine";
private static final Map<String, String> DEFAULT_FLAGS;
@@ -247,6 +253,7 @@
DEFAULT_FLAGS.put(SETTINGS_BIOMETRICS2_FINGERPRINT_SETTINGS, "false");
// TODO: b/298454866 Replace with Trunk Stable Feature Flag
DEFAULT_FLAGS.put(SETTINGS_REMOTEAUTH_ENROLLMENT_SETTINGS, "false");
+ DEFAULT_FLAGS.put(SETTINGS_TREAT_PAUSE_AS_QUARANTINE, "false");
}
private static final Set<String> PERSISTENT_FLAGS;
@@ -264,6 +271,7 @@
PERSISTENT_FLAGS.add(SETTINGS_ENABLE_SPA);
PERSISTENT_FLAGS.add(SETTINGS_ENABLE_SPA_PHASE2);
PERSISTENT_FLAGS.add(SETTINGS_PREFER_ACCESSIBILITY_MENU_IN_SYSTEM);
+ PERSISTENT_FLAGS.add(SETTINGS_TREAT_PAUSE_AS_QUARANTINE);
}
/**
diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java
index 1ed5d3f..71d382e 100644
--- a/core/java/android/view/AttachedSurfaceControl.java
+++ b/core/java/android/view/AttachedSurfaceControl.java
@@ -92,6 +92,12 @@
* SurfaceView Surface, the buffer producer will already have access to the transform hint and
* no additional work is needed.
*
+ * If the root surface is not available, the API will return {@code BUFFER_TRANSFORM_IDENTITY}.
+ * The caller should register a listener to listen for any changes. @see
+ * {@link #addOnBufferTransformHintChangedListener(OnBufferTransformHintChangedListener)}.
+ * Warning: Calling this API in Android 14 (API Level 34) or earlier will crash if the root
+ * surface is not available.
+ *
* @see HardwareBuffer
*/
default @SurfaceControl.BufferTransform int getBufferTransformHint() {
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 5069455..17c7fcc 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -2656,6 +2656,7 @@
if (displayId == getDisplayId()) {
float newRatio = getHdrSdrRatio();
if (newRatio != mLastReportedRatio) {
+ mLastReportedRatio = newRatio;
mListener.accept(Display.this);
}
}
diff --git a/core/java/android/view/SurfaceControlRegistry.java b/core/java/android/view/SurfaceControlRegistry.java
index 67ac811..0f35048 100644
--- a/core/java/android/view/SurfaceControlRegistry.java
+++ b/core/java/android/view/SurfaceControlRegistry.java
@@ -78,6 +78,11 @@
for (int i = 0; i < size; i++) {
final Map.Entry<SurfaceControl, Long> entry = entries.get(i);
final SurfaceControl sc = entry.getKey();
+ if (sc == null) {
+ // Just skip if the key has since been removed from the weak hash map
+ continue;
+ }
+
final long timeRegistered = entry.getValue();
pw.print(" ");
pw.print(sc.getName());
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e9d0e4c..49b16c7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -19,6 +19,8 @@
import static android.content.res.Resources.ID_NULL;
import static android.os.Trace.TRACE_TAG_APP;
import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
+import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
+import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
@@ -27,6 +29,7 @@
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH;
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
+import static android.view.flags.Flags.toolkitSetFrameRate;
import static android.view.flags.Flags.viewVelocityApi;
import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS;
@@ -114,6 +117,7 @@
import android.text.InputType;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.util.FloatProperty;
import android.util.LayoutDirection;
import android.util.Log;
@@ -5509,6 +5513,11 @@
private ViewTranslationResponse mViewTranslationResponse;
/**
+ * A threshold value to determine the frame rate category of the View based on the size.
+ */
+ private static final float FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD = 0.07f;
+
+ /**
* Simple constructor to use when creating a view from code.
*
* @param context The Context the view is running in, through which it can
@@ -20183,6 +20192,9 @@
return;
}
+ // For VRR to vote the preferred frame rate
+ votePreferredFrameRate();
+
// Reset content capture caches
mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK;
mContentCaptureSessionCached = false;
@@ -20285,6 +20297,8 @@
*/
protected void damageInParent() {
if (mParent != null && mAttachInfo != null) {
+ // For VRR to vote the preferred frame rate
+ votePreferredFrameRate();
mParent.onDescendantInvalidated(this, this);
}
}
@@ -32981,6 +32995,40 @@
return null;
}
+ private float getSizePercentage() {
+ if (mResources == null || getAlpha() == 0 || getVisibility() != VISIBLE) {
+ return 0;
+ }
+
+ DisplayMetrics displayMetrics = mResources.getDisplayMetrics();
+ int screenSize = displayMetrics.widthPixels
+ * displayMetrics.heightPixels;
+ int viewSize = getWidth() * getHeight();
+
+ if (screenSize == 0 || viewSize == 0) {
+ return 0f;
+ }
+ return (float) viewSize / screenSize;
+ }
+
+ private int calculateFrameRateCategory() {
+ float sizePercentage = getSizePercentage();
+
+ if (sizePercentage <= FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD) {
+ return FRAME_RATE_CATEGORY_LOW;
+ } else {
+ return FRAME_RATE_CATEGORY_NORMAL;
+ }
+ }
+
+ private void votePreferredFrameRate() {
+ // use toolkitSetFrameRate flag to gate the change
+ ViewRootImpl viewRootImpl = getViewRootImpl();
+ if (toolkitSetFrameRate() && viewRootImpl != null && getSizePercentage() > 0) {
+ viewRootImpl.votePreferredFrameRateCategory(calculateFrameRateCategory());
+ }
+ }
+
/**
* Set the current velocity of the View, we only track positive value.
* We will use the velocity information to adjust the frame rate when applicable.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9392783..50eeed8 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -24,6 +24,8 @@
import static android.view.Display.INVALID_DISPLAY;
import static android.view.InputDevice.SOURCE_CLASS_NONE;
import static android.view.InsetsSource.ID_IME;
+import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
+import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
import static android.view.View.PFLAG_DRAW_ANIMATION;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
@@ -74,7 +76,10 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
@@ -87,6 +92,7 @@
import static android.view.accessibility.Flags.forceInvertColor;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER;
+import static android.view.flags.Flags.toolkitSetFrameRate;
import android.Manifest;
import android.accessibilityservice.AccessibilityService;
@@ -732,6 +738,7 @@
private SurfaceControl mBoundsLayer;
private final SurfaceSession mSurfaceSession = new SurfaceSession();
private final Transaction mTransaction = new Transaction();
+ private final Transaction mFrameRateTransaction = new Transaction();
@UnsupportedAppUsage
boolean mAdded;
@@ -955,6 +962,34 @@
private AccessibilityWindowAttributes mAccessibilityWindowAttributes;
+ /*
+ * for Variable Refresh Rate project
+ */
+
+ // The preferred frame rate category of the view that
+ // could be updated on a frame-by-frame basis.
+ private int mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ // The preferred frame rate category of the last frame that
+ // could be used to lower frame rate after touch boost
+ private int mLastPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ // The preferred frame rate of the view that is mainly used for
+ // touch boosting, view velocity handling, and TextureView.
+ private float mPreferredFrameRate = 0;
+ // Used to check if there were any view invalidations in
+ // the previous time frame (FRAME_RATE_IDLENESS_REEVALUATE_TIME).
+ private boolean mHasInvalidation = false;
+ // Used to check if it is in the touch boosting period.
+ private boolean mIsFrameRateBoosting = false;
+ // Used to check if there is a message in the message queue
+ // for idleness handling.
+ private boolean mHasIdledMessage = false;
+ // time for touch boost period.
+ private static final int FRAME_RATE_TOUCH_BOOST_TIME = 1500;
+ // time for checking idle status periodically.
+ private static final int FRAME_RATE_IDLENESS_CHECK_TIME_MILLIS = 500;
+ // time for revaluating the idle status before lowering the frame rate.
+ private static final int FRAME_RATE_IDLENESS_REEVALUATE_TIME = 500;
+
/**
* A temporary object used so relayoutWindow can return the latest SyncSeqId
* system. The SyncSeqId system was designed to work without synchronous relayout
@@ -1016,7 +1051,8 @@
mDisplay = display;
mBasePackageName = context.getBasePackageName();
final String name = DisplayProperties.debug_vri_package().orElse(null);
- mExtraDisplayListenerLogging = !TextUtils.isEmpty(name) && name.equals(mBasePackageName);
+ // TODO: b/306170135 - return to using textutils check on package name.
+ mExtraDisplayListenerLogging = true;
mThread = Thread.currentThread();
mLocation = new WindowLeaked(null);
mLocation.fillInStackTrace();
@@ -2003,6 +2039,10 @@
Slog.i(mTag, "DisplayState - old: " + oldDisplayState
+ ", new: " + newDisplayState);
}
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.traceCounter(Trace.TRACE_TAG_WINDOW_MANAGER,
+ "vri#screenState[" + mTag + "] state=", newDisplayState);
+ }
if (oldDisplayState != newDisplayState) {
mAttachInfo.mDisplayState = newDisplayState;
pokeDrawLockIfNeeded();
@@ -3918,6 +3958,12 @@
mWmsRequestSyncGroupState = WMS_SYNC_NONE;
}
}
+
+ // For the variable refresh rate project.
+ setPreferredFrameRate(mPreferredFrameRate);
+ setPreferredFrameRateCategory(mPreferredFrameRateCategory);
+ mLastPreferredFrameRateCategory = mPreferredFrameRateCategory;
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
}
private void createSyncIfNeeded() {
@@ -5970,6 +6016,8 @@
private static final int MSG_REPORT_KEEP_CLEAR_RECTS = 36;
private static final int MSG_PAUSED_FOR_SYNC_TIMEOUT = 37;
private static final int MSG_DECOR_VIEW_GESTURE_INTERCEPTION = 38;
+ private static final int MSG_TOUCH_BOOST_TIMEOUT = 39;
+ private static final int MSG_CHECK_INVALIDATION_IDLE = 40;
final class ViewRootHandler extends Handler {
@Override
@@ -6265,6 +6313,32 @@
mNumPausedForSync = 0;
scheduleTraversals();
break;
+ case MSG_TOUCH_BOOST_TIMEOUT:
+ /**
+ * Lower the frame rate after the boosting period (FRAME_RATE_TOUCH_BOOST_TIME).
+ */
+ mIsFrameRateBoosting = false;
+ setPreferredFrameRateCategory(Math.max(mPreferredFrameRateCategory,
+ mLastPreferredFrameRateCategory));
+ break;
+ case MSG_CHECK_INVALIDATION_IDLE:
+ if (!mHasInvalidation && !mIsFrameRateBoosting) {
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ setPreferredFrameRateCategory(mPreferredFrameRateCategory);
+ mHasIdledMessage = false;
+ } else {
+ /**
+ * If there is no invalidation within a certain period,
+ * we consider the display is idled.
+ * We then set the frame rate catetogry to NO_PREFERENCE.
+ * Note that SurfaceFlinger also has a mechanism to lower the refresh rate
+ * if there is no updates of the buffer.
+ */
+ mHasInvalidation = false;
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
+ FRAME_RATE_IDLENESS_REEVALUATE_TIME);
+ }
+ break;
}
}
}
@@ -7207,6 +7281,7 @@
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
+ final int action = event.getAction();
boolean handled = mHandwritingInitiator.onTouchEvent(event);
if (handled) {
// If handwriting is started, toolkit doesn't receive ACTION_UP.
@@ -7227,6 +7302,22 @@
scheduleConsumeBatchedInputImmediately();
}
}
+
+ // For the variable refresh rate project
+ if (handled && shouldTouchBoost(action, mWindowAttributes.type)) {
+ // set the frame rate to the maximum value.
+ mIsFrameRateBoosting = true;
+ setPreferredFrameRateCategory(mPreferredFrameRateCategory);
+ }
+ /**
+ * We want to lower the refresh rate when MotionEvent.ACTION_UP,
+ * MotionEvent.ACTION_CANCEL is detected.
+ * Not using ACTION_MOVE to avoid checking and sending messages too frequently.
+ */
+ if (mIsFrameRateBoosting && (action == MotionEvent.ACTION_UP
+ || action == MotionEvent.ACTION_CANCEL)) {
+ sendDelayedEmptyMessage(MSG_TOUCH_BOOST_TIMEOUT, FRAME_RATE_TOUCH_BOOST_TIME);
+ }
return handled ? FINISH_HANDLED : FORWARD;
}
@@ -11441,7 +11532,11 @@
@Override
public @SurfaceControl.BufferTransform int getBufferTransformHint() {
- return mSurfaceControl.getTransformHint();
+ if (mSurfaceControl.isValid()) {
+ return mSurfaceControl.getTransformHint();
+ } else {
+ return SurfaceControl.BUFFER_TRANSFORM_IDENTITY;
+ }
}
@Override
@@ -11810,4 +11905,94 @@
@NonNull Consumer<Boolean> listener) {
t.clearTrustedPresentationCallback(getSurfaceControl());
}
+
+ private void setPreferredFrameRateCategory(int preferredFrameRateCategory) {
+ if (!shouldSetFrameRateCategory()) {
+ return;
+ }
+
+ int frameRateCategory = mIsFrameRateBoosting
+ ? FRAME_RATE_CATEGORY_HIGH : preferredFrameRateCategory;
+
+ try {
+ mFrameRateTransaction.setFrameRateCategory(mSurfaceControl,
+ frameRateCategory, false).apply();
+ } catch (Exception e) {
+ Log.e(mTag, "Unable to set frame rate category", e);
+ }
+
+ if (mPreferredFrameRateCategory != FRAME_RATE_CATEGORY_NO_PREFERENCE && !mHasIdledMessage) {
+ // Check where the display is idled periodically.
+ // If so, set the frame rate category to NO_PREFERENCE
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
+ FRAME_RATE_IDLENESS_CHECK_TIME_MILLIS);
+ mHasIdledMessage = true;
+ }
+ }
+
+ private void setPreferredFrameRate(float preferredFrameRate) {
+ if (!shouldSetFrameRate()) {
+ return;
+ }
+
+ try {
+ mFrameRateTransaction.setFrameRate(mSurfaceControl,
+ preferredFrameRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT).apply();
+ } catch (Exception e) {
+ Log.e(mTag, "Unable to set frame rate", e);
+ }
+ }
+
+ private void sendDelayedEmptyMessage(int message, int delayedTime) {
+ mHandler.removeMessages(message);
+
+ mHandler.sendEmptyMessageDelayed(message, delayedTime);
+ }
+
+ private boolean shouldSetFrameRateCategory() {
+ // use toolkitSetFrameRate flag to gate the change
+ return mSurface.isValid() && toolkitSetFrameRate();
+ }
+
+ private boolean shouldSetFrameRate() {
+ // use toolkitSetFrameRate flag to gate the change
+ return mPreferredFrameRate > 0 && toolkitSetFrameRate();
+ }
+
+ private boolean shouldTouchBoost(int motionEventAction, int windowType) {
+ boolean desiredAction = motionEventAction == MotionEvent.ACTION_DOWN
+ || motionEventAction == MotionEvent.ACTION_MOVE
+ || motionEventAction == MotionEvent.ACTION_UP;
+ boolean desiredType = windowType == TYPE_BASE_APPLICATION || windowType == TYPE_APPLICATION
+ || windowType == TYPE_APPLICATION_STARTING || windowType == TYPE_DRAWN_APPLICATION;
+ // use toolkitSetFrameRate flag to gate the change
+ return desiredAction && desiredType && toolkitSetFrameRate();
+ }
+
+ /**
+ * Allow Views to vote for the preferred frame rate category
+ *
+ * @param frameRateCategory the preferred frame rate category of a View
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
+ public void votePreferredFrameRateCategory(int frameRateCategory) {
+ mPreferredFrameRateCategory = Math.max(mPreferredFrameRateCategory, frameRateCategory);
+ mHasInvalidation = true;
+ }
+
+ /**
+ * Get the value of mPreferredFrameRateCategory
+ */
+ @VisibleForTesting
+ public int getPreferredFrameRateCategory() {
+ return mPreferredFrameRateCategory;
+ }
+
+ /**
+ * Get the value of mPreferredFrameRate
+ */
+ @VisibleForTesting
+ public float getPreferredFrameRate() {
+ return mPreferredFrameRate;
+ }
}
diff --git a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
index 5c86feb..f6ee061 100644
--- a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
+++ b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
@@ -13,3 +13,10 @@
description: "If true, content protection groups config will be parsed."
bug: "302187922"
}
+
+flag {
+ name: "setting_ui_enabled"
+ namespace: "content_protection"
+ description: "If true, content protection setting ui is displayed in Settings > Privacy & Security > More security & privacy."
+ bug: "305792348"
+}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 8159af3..eeab005 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -39,6 +39,7 @@
import android.annotation.DisplayContext;
import android.annotation.DrawableRes;
import android.annotation.DurationMillisLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1512,6 +1513,7 @@
* Returns {@code true} if currently selected IME supports Stylus handwriting & is enabled.
* If the method returns {@code false}, {@link #startStylusHandwriting(View)} shouldn't be
* called and Stylus touch should continue as normal touch input.
+ *
* @see #startStylusHandwriting(View)
*/
public boolean isStylusHandwritingAvailable() {
@@ -1535,6 +1537,7 @@
@NonNull
@RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
@TestApi
+ @FlaggedApi(Flags.FLAG_IMM_USERHANDLE_HOSTSIDETESTS)
@SuppressLint("UserHandle")
public boolean isStylusHandwritingAvailableAsUser(@NonNull UserHandle user) {
final Context fallbackContext = ActivityThread.currentApplication();
@@ -1655,6 +1658,7 @@
@NonNull
@RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
@TestApi
+ @FlaggedApi(Flags.FLAG_IMM_USERHANDLE_HOSTSIDETESTS)
@SuppressLint("UserHandle")
public List<InputMethodInfo> getEnabledInputMethodListAsUser(@NonNull UserHandle user) {
return IInputMethodManagerGlobalInvoker.getEnabledInputMethodList(user.getIdentifier());
@@ -1690,12 +1694,13 @@
* {@link Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required if this is
* different from the calling process user ID.
* @return {@link List} of {@link InputMethodSubtype}.
- * @see #getEnabledInputMethodListAsUser(int)
+ * @see #getEnabledInputMethodListAsUser(UserHandle)
* @hide
*/
@NonNull
@RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
@TestApi
+ @FlaggedApi(Flags.FLAG_IMM_USERHANDLE_HOSTSIDETESTS)
@SuppressLint("UserHandle")
public List<InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser(
@NonNull String imeId, boolean allowsImplicitlyEnabledSubtypes,
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index c14b510..1e8718c 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -14,4 +14,12 @@
description: "Feature flag for adding EditorInfo#mStylusHandwritingEnabled"
bug: "293898187"
is_fixed_read_only: true
+}
+
+flag {
+ name: "imm_userhandle_hostsidetests"
+ namespace: "input_method"
+ description: "Feature flag for replacing UserIdInt with UserHandle in some helper IMM functions"
+ bug: "301713309"
+ is_fixed_read_only: true
}
\ No newline at end of file
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 103725d..f19a2f9 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -685,8 +685,9 @@
return false;
}
+ /** See {@link RemoteViews#visitUris(Consumer)}. **/
public void visitUris(@NonNull Consumer<Uri> visitor) {
- // Nothing to visit by default
+ // Nothing to visit by default.
}
}
@@ -761,9 +762,11 @@
}
/**
- * Note all {@link Uri} that are referenced internally, with the expectation
- * that Uri permission grants will need to be issued to ensure the recipient
- * of this object is able to render its contents.
+ * Note all {@link Uri} that are referenced internally, with the expectation that Uri permission
+ * grants will need to be issued to ensure the recipient of this object is able to render its
+ * contents.
+ * See b/281044385 for more context and examples about what happens when this isn't done
+ * correctly.
*
* @hide
*/
@@ -1088,6 +1091,13 @@
public String getUniqueKey() {
return (SET_REMOTE_ADAPTER_TAG + "_" + mViewId);
}
+
+ @Override
+ public void visitUris(@NonNull Consumer<Uri> visitor) {
+ for (RemoteViews remoteViews : mList) {
+ remoteViews.visitUris(visitor);
+ }
+ }
}
/**
@@ -1289,6 +1299,12 @@
public String getUniqueKey() {
return (SET_REMOTE_ADAPTER_TAG + "_" + mViewId);
}
+
+ @Override
+ public void visitUris(@NonNull Consumer<Uri> visitor) {
+ RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
+ items.visitUris(visitor);
+ }
}
private class SetRemoteViewsAdapterIntent extends Action {
@@ -1359,6 +1375,13 @@
public int getActionTag() {
return SET_REMOTE_VIEW_ADAPTER_INTENT_TAG;
}
+
+ @Override
+ public void visitUris(@NonNull Consumer<Uri> visitor) {
+ // TODO(b/281044385): Maybe visit intent URIs. This may require adding a dedicated
+ // visitUris method in the Intent class, since it can contain other intents. Otherwise,
+ // the basic thing to do here would be just visitor.accept(intent.getData()).
+ }
}
/**
@@ -1434,6 +1457,11 @@
public int getActionTag() {
return SET_ON_CLICK_RESPONSE_TAG;
}
+
+ @Override
+ public void visitUris(@NonNull Consumer<Uri> visitor) {
+ // TODO(b/281044385): Maybe visit intent URIs in the RemoteResponse.
+ }
}
/**
@@ -1504,6 +1532,11 @@
public int getActionTag() {
return SET_ON_CHECKED_CHANGE_RESPONSE_TAG;
}
+
+ @Override
+ public void visitUris(@NonNull Consumer<Uri> visitor) {
+ // TODO(b/281044385): Maybe visit intent URIs in the RemoteResponse.
+ }
}
/** @hide **/
@@ -2063,6 +2096,7 @@
final Icon icon = (Icon) getParameterValue(null);
if (icon != null) visitIconUri(icon, visitor);
break;
+ // TODO(b/281044385): Should we do anything about type BUNDLE?
}
}
}
@@ -2812,7 +2846,7 @@
}
@Override
- public final void visitUris(@NonNull Consumer<Uri> visitor) {
+ public void visitUris(@NonNull Consumer<Uri> visitor) {
mNestedViews.visitUris(visitor);
}
}
@@ -7262,6 +7296,15 @@
Math.max(mViewTypeCount, 1));
}
}
+
+ /**
+ * See {@link RemoteViews#visitUris(Consumer)}.
+ */
+ private void visitUris(@NonNull Consumer<Uri> visitor) {
+ for (RemoteViews view : mViews) {
+ view.visitUris(visitor);
+ }
+ }
}
/**
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index e8281ea..17c82b6 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -15555,6 +15555,9 @@
private void ensureIterableTextForAccessibilitySelectable() {
if (!(mText instanceof Spannable)) {
setText(mText, BufferType.SPANNABLE);
+ if (getLayout() == null) {
+ assumeLayout();
+ }
}
}
diff --git a/core/java/android/window/SurfaceSyncGroup.java b/core/java/android/window/SurfaceSyncGroup.java
index eb9d62b..5d14698 100644
--- a/core/java/android/window/SurfaceSyncGroup.java
+++ b/core/java/android/window/SurfaceSyncGroup.java
@@ -263,7 +263,6 @@
Trace.instantForTrack(Trace.TRACE_TAG_VIEW, mTrackName, "markSyncReady");
}
synchronized (mLock) {
- toggleTimeout(false);
if (mHasWMSync) {
try {
WindowManagerGlobal.getWindowManagerService().markSurfaceSyncGroupReady(mToken);
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 61f340a..1a2d202 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -402,6 +402,18 @@
@Override
public String toString() {
+ return toString("");
+ }
+
+ /**
+ * Returns a string representation of this transition info.
+ * @hide
+ */
+ public String toString(@NonNull String prefix) {
+ final boolean shouldPrettyPrint = !prefix.isEmpty() && !mChanges.isEmpty();
+ final String innerPrefix = shouldPrettyPrint ? prefix + " " : "";
+ final String changesLineStart = shouldPrettyPrint ? "\n" + prefix : "";
+ final String perChangeLineStart = shouldPrettyPrint ? "\n" + innerPrefix : "";
StringBuilder sb = new StringBuilder();
sb.append("{id=").append(mDebugId).append(" t=").append(transitTypeToString(mType))
.append(" f=0x").append(Integer.toHexString(mFlags)).append(" trk=").append(mTrack)
@@ -413,12 +425,15 @@
sb.append(mRoots.get(i).mDisplayId).append("@").append(mRoots.get(i).mOffset);
}
sb.append("] c=[");
+ sb.append(perChangeLineStart);
for (int i = 0; i < mChanges.size(); ++i) {
if (i > 0) {
sb.append(',');
+ sb.append(perChangeLineStart);
}
sb.append(mChanges.get(i));
}
+ sb.append(changesLineStart);
sb.append("]}");
return sb.toString();
}
diff --git a/core/java/android/window/TransitionRequestInfo.java b/core/java/android/window/TransitionRequestInfo.java
index 932608a3..bd54e14 100644
--- a/core/java/android/window/TransitionRequestInfo.java
+++ b/core/java/android/window/TransitionRequestInfo.java
@@ -62,13 +62,16 @@
/** The transition flags known at the time of the request. These may not be complete. */
private final int mFlags;
+ /** This is only a BEST-EFFORT id used for log correlation. DO NOT USE for any real work! */
+ private final int mDebugId;
+
/** constructor override */
public TransitionRequestInfo(
@WindowManager.TransitionType int type,
@Nullable ActivityManager.RunningTaskInfo triggerTask,
@Nullable RemoteTransition remoteTransition) {
this(type, triggerTask, null /* pipTask */,
- remoteTransition, null /* displayChange */, 0 /* flags */);
+ remoteTransition, null /* displayChange */, 0 /* flags */, -1 /* debugId */);
}
/** constructor override */
@@ -78,16 +81,29 @@
@Nullable RemoteTransition remoteTransition,
int flags) {
this(type, triggerTask, null /* pipTask */,
- remoteTransition, null /* displayChange */, flags);
+ remoteTransition, null /* displayChange */, flags, -1 /* debugId */);
}
+ /** constructor override */
public TransitionRequestInfo(
@WindowManager.TransitionType int type,
@Nullable ActivityManager.RunningTaskInfo triggerTask,
@Nullable RemoteTransition remoteTransition,
@Nullable TransitionRequestInfo.DisplayChange displayChange,
int flags) {
- this(type, triggerTask, null /* pipTask */, remoteTransition, displayChange, flags);
+ this(type, triggerTask, null /* pipTask */, remoteTransition, displayChange, flags,
+ -1 /* debugId */);
+ }
+
+ /** constructor override */
+ public TransitionRequestInfo(
+ @WindowManager.TransitionType int type,
+ @Nullable ActivityManager.RunningTaskInfo triggerTask,
+ @Nullable ActivityManager.RunningTaskInfo pipTask,
+ @Nullable RemoteTransition remoteTransition,
+ @Nullable TransitionRequestInfo.DisplayChange displayChange,
+ int flags) {
+ this(type, triggerTask, pipTask, remoteTransition, displayChange, flags, -1 /* debugId */);
}
/** @hide */
@@ -270,7 +286,7 @@
};
@DataClass.Generated(
- time = 1695667226050L,
+ time = 1697564781403L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
inputSignatures = "private final int mDisplayId\nprivate @android.annotation.Nullable android.graphics.Rect mStartAbsBounds\nprivate @android.annotation.Nullable android.graphics.Rect mEndAbsBounds\nprivate int mStartRotation\nprivate int mEndRotation\nprivate boolean mPhysicalDisplayChanged\nclass DisplayChange extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genBuilder=false, genConstructor=false)")
@@ -318,6 +334,8 @@
* (if size is changing).
* @param flags
* The transition flags known at the time of the request. These may not be complete.
+ * @param debugId
+ * This is only a BEST-EFFORT id used for log correlation. DO NOT USE for any real work!
*/
@DataClass.Generated.Member
public TransitionRequestInfo(
@@ -326,7 +344,8 @@
@Nullable ActivityManager.RunningTaskInfo pipTask,
@Nullable RemoteTransition remoteTransition,
@Nullable TransitionRequestInfo.DisplayChange displayChange,
- int flags) {
+ int flags,
+ int debugId) {
this.mType = type;
com.android.internal.util.AnnotationValidations.validate(
WindowManager.TransitionType.class, null, mType);
@@ -335,6 +354,7 @@
this.mRemoteTransition = remoteTransition;
this.mDisplayChange = displayChange;
this.mFlags = flags;
+ this.mDebugId = debugId;
// onConstructed(); // You can define this method to get a callback
}
@@ -392,6 +412,14 @@
}
/**
+ * This is only a BEST-EFFORT id used for log correlation. DO NOT USE for any real work!
+ */
+ @DataClass.Generated.Member
+ public int getDebugId() {
+ return mDebugId;
+ }
+
+ /**
* If non-null, the task containing the activity whose lifecycle change (start or
* finish) has caused this transition to occur.
*/
@@ -443,7 +471,8 @@
"pipTask = " + mPipTask + ", " +
"remoteTransition = " + mRemoteTransition + ", " +
"displayChange = " + mDisplayChange + ", " +
- "flags = " + mFlags +
+ "flags = " + mFlags + ", " +
+ "debugId = " + mDebugId +
" }";
}
@@ -465,6 +494,7 @@
if (mRemoteTransition != null) dest.writeTypedObject(mRemoteTransition, flags);
if (mDisplayChange != null) dest.writeTypedObject(mDisplayChange, flags);
dest.writeInt(mFlags);
+ dest.writeInt(mDebugId);
}
@Override
@@ -485,6 +515,7 @@
RemoteTransition remoteTransition = (flg & 0x8) == 0 ? null : (RemoteTransition) in.readTypedObject(RemoteTransition.CREATOR);
TransitionRequestInfo.DisplayChange displayChange = (flg & 0x10) == 0 ? null : (TransitionRequestInfo.DisplayChange) in.readTypedObject(TransitionRequestInfo.DisplayChange.CREATOR);
int flags = in.readInt();
+ int debugId = in.readInt();
this.mType = type;
com.android.internal.util.AnnotationValidations.validate(
@@ -494,6 +525,7 @@
this.mRemoteTransition = remoteTransition;
this.mDisplayChange = displayChange;
this.mFlags = flags;
+ this.mDebugId = debugId;
// onConstructed(); // You can define this method to get a callback
}
@@ -513,10 +545,10 @@
};
@DataClass.Generated(
- time = 1695667226088L,
+ time = 1697564781438L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
- inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mPipTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\n java.lang.String typeToString()\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
+ inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mPipTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\nprivate final int mDebugId\n java.lang.String typeToString()\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 392aa1b..7cfd35b 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -18,6 +18,22 @@
name: "dimmer_refactor"
namespace: "windowing_frontend"
description: "Refactor dim to fix flickers"
- bug: "281632483,295291019"
+ bug: "295291019"
is_fixed_read_only: true
+}
+
+flag {
+ name: "transit_ready_tracking"
+ namespace: "windowing_frontend"
+ description: "Enable accurate transition readiness tracking"
+ bug: "294925498"
+}
+
+
+flag {
+ name: "wallpaper_offset_async"
+ namespace: "windowing_frontend"
+ description: "Do not synchronise the wallpaper offset"
+ bug: "293248754"
+ is_fixed_read_only: true
}
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index 4e7bfe5..71bbccb 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -259,7 +259,7 @@
}
return true;
}
- return false;
+ return super.onKeyDown(keyCode,event);
}
@Override
@@ -268,7 +268,7 @@
stopWarp();
return true;
}
- return false;
+ return super.onKeyUp(keyCode,event);
}
private void startWarp() {
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 6e836e0..7e9cef7 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -40,6 +40,7 @@
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_SEARCH_RESULT;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNFOLD_ANIM;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_CLOCK_MOVE_ANIMATION;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_LAUNCH_CAMERA;
@@ -273,7 +274,9 @@
public static final int CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER = 82;
- private static final int LAST_CUJ = CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER;
+ public static final int CUJ_LAUNCHER_UNFOLD_ANIM = 83;
+
+ private static final int LAST_CUJ = CUJ_LAUNCHER_UNFOLD_ANIM;
private static final int NO_STATSD_LOGGING = -1;
// Used to convert CujType to InteractionType enum value for statsd logging.
@@ -366,6 +369,7 @@
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_IME_INSETS_SHOW_ANIMATION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_SHOW_ANIMATION;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_IME_INSETS_HIDE_ANIMATION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_HIDE_ANIMATION;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_DOUBLE_TAP_DIVIDER;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_UNFOLD_ANIM] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNFOLD_ANIM;
}
private static class InstanceHolder {
@@ -468,6 +472,7 @@
CUJ_IME_INSETS_SHOW_ANIMATION,
CUJ_IME_INSETS_HIDE_ANIMATION,
CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER,
+ CUJ_LAUNCHER_UNFOLD_ANIM,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -1101,6 +1106,8 @@
return "IME_INSETS_HIDE_ANIMATION";
case CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER:
return "SPLIT_SCREEN_DOUBLE_TAP_DIVIDER";
+ case CUJ_LAUNCHER_UNFOLD_ANIM:
+ return "LAUNCHER_UNFOLD_ANIM";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/policy/WearGestureInterceptionDetector.java b/core/java/com/android/internal/policy/WearGestureInterceptionDetector.java
index 6fd5018..504928c 100644
--- a/core/java/com/android/internal/policy/WearGestureInterceptionDetector.java
+++ b/core/java/com/android/internal/policy/WearGestureInterceptionDetector.java
@@ -74,16 +74,15 @@
return windowSwipeToDismiss;
}
- private boolean isPointerIndexValid(MotionEvent ev) {
+ private int getIndexForValidPointer(MotionEvent ev) {
int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) {
if (DEBUG) {
Log.e(TAG, "Invalid pointer index: ignoring.");
}
mDiscardIntercept = true;
- return false;
}
- return true;
+ return pointerIndex;
}
private void updateSwiping(MotionEvent ev) {
@@ -98,7 +97,7 @@
}
}
- private void updateDiscardIntercept(MotionEvent ev) {
+ private void updateDiscardIntercept(MotionEvent ev, int pointerIndex) {
if (!mSwiping) {
// Don't look at canScroll until we have passed the touch slop
return;
@@ -107,8 +106,8 @@
return;
}
final boolean checkLeft = mDownX < ev.getRawX();
- final float x = ev.getX(mActivePointerId);
- final float y = ev.getY(mActivePointerId);
+ final float x = ev.getX(pointerIndex);
+ final float y = ev.getY(pointerIndex);
if (canScroll(mInstalledDecorView, false, checkLeft, x, y)) {
mDiscardIntercept = true;
}
@@ -152,11 +151,12 @@
if (mDiscardIntercept) {
break;
}
- if (!isPointerIndexValid(ev)) {
+ final int pointerIndex = getIndexForValidPointer(ev);
+ if (pointerIndex == -1) {
break;
}
updateSwiping(ev);
- updateDiscardIntercept(ev);
+ updateDiscardIntercept(ev, pointerIndex);
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 4065055..8236783 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -108,4 +108,5 @@
boolean removeWeakEscrowToken(long handle, int userId);
boolean isWeakEscrowTokenActive(long handle, int userId);
boolean isWeakEscrowTokenValid(long handle, in byte[] token, int userId);
+ void unlockUserKeyIfUnsecured(int userId);
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index d5b8f62..a3e2706 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1933,8 +1933,23 @@
}
}
+ /**
+ * Unlocks the credential-encrypted storage for the given user if the user is not secured, i.e.
+ * doesn't have an LSKF.
+ * <p>
+ * Whether the storage has been unlocked can be determined by
+ * {@link StorageManager#isUserKeyUnlocked()}.
+ *
+ * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+ *
+ * @param userId the ID of the user whose storage to unlock
+ */
public void unlockUserKeyIfUnsecured(@UserIdInt int userId) {
- getLockSettingsInternal().unlockUserKeyIfUnsecured(userId);
+ try {
+ getLockSettings().unlockUserKeyIfUnsecured(userId);
+ } catch (RemoteException re) {
+ re.rethrowFromSystemServer();
+ }
}
public void createNewUser(@UserIdInt int userId, int userSerialNumber) {
diff --git a/core/java/com/android/internal/widget/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java
index 6063c90..8114e1f 100644
--- a/core/java/com/android/internal/widget/LockSettingsInternal.java
+++ b/core/java/com/android/internal/widget/LockSettingsInternal.java
@@ -60,17 +60,6 @@
public abstract void onThirdPartyAppsStarted();
/**
- * Unlocks the credential-encrypted storage for the given user if the user is not secured, i.e.
- * doesn't have an LSKF.
- * <p>
- * This doesn't throw an exception on failure; whether the storage has been unlocked can be
- * determined by {@link StorageManager#isUserKeyUnlocked()}.
- *
- * @param userId the ID of the user whose storage to unlock
- */
- public abstract void unlockUserKeyIfUnsecured(@UserIdInt int userId);
-
- /**
* Creates the locksettings state for a new user.
* <p>
* This includes creating a synthetic password and protecting it with an empty LSKF.
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index deb138f..9c883d1 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -265,6 +265,18 @@
return mgr->isDataInjectionEnabled();
}
+static jboolean nativeIsReplayDataInjectionEnabled(JNIEnv *_env, jclass _this,
+ jlong sensorManager) {
+ SensorManager *mgr = reinterpret_cast<SensorManager *>(sensorManager);
+ return mgr->isReplayDataInjectionEnabled();
+}
+
+static jboolean nativeIsHalBypassReplayDataInjectionEnabled(JNIEnv *_env, jclass _this,
+ jlong sensorManager) {
+ SensorManager *mgr = reinterpret_cast<SensorManager *>(sensorManager);
+ return mgr->isHalBypassReplayDataInjectionEnabled();
+}
+
static jint nativeCreateDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager,
jint deviceId, jlong size, jint channelType, jint fd,
jobject hardwareBufferObj) {
@@ -533,6 +545,11 @@
{"nativeIsDataInjectionEnabled", "(J)Z", (void *)nativeIsDataInjectionEnabled},
+ {"nativeIsReplayDataInjectionEnabled", "(J)Z", (void *)nativeIsReplayDataInjectionEnabled},
+
+ {"nativeIsHalBypassReplayDataInjectionEnabled", "(J)Z",
+ (void *)nativeIsHalBypassReplayDataInjectionEnabled},
+
{"nativeCreateDirectChannel", "(JIJIILandroid/hardware/HardwareBuffer;)I",
(void *)nativeCreateDirectChannel},
diff --git a/core/res/Android.bp b/core/res/Android.bp
index b71995f..4e686b7 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -104,6 +104,7 @@
android_app {
name: "framework-res",
+ use_resource_processor: false,
sdk_version: "core_platform",
certificate: "platform",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 88b578b..6daa5b9 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5668,7 +5668,8 @@
android:description="@string/permdesc_deliverCompanionMessages"
android:protectionLevel="normal" />
- <!-- @hide @SystemApi(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+ <!-- @hide @FlaggedApi("android.companion.flags.companion_transport_apis")
+ @SystemApi(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES)
Allows an application to send and receive messages via CDM transports.
-->
<permission android:name="android.permission.USE_COMPANION_TRANSPORTS"
@@ -6123,7 +6124,7 @@
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<!-- @SystemApi @hide
- @FlaggedApi("backstage_power.report_usage_stats_permission")
+ @FlaggedApi("android.app.usage.report_usage_stats_permission")
Allows trusted system components to report events to UsageStatsManager -->
<permission android:name="android.permission.REPORT_USAGE_STATS"
android:protectionLevel="signature|module" />
@@ -7143,7 +7144,7 @@
{@link ActivityOptions#makeRemoteAnimation}
@hide <p>Not for use by third-party applications. -->
<permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|recents" />
<!-- Allows an application to watch changes and/or active state of app ops.
@hide <p>Not for use by third-party applications. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b211ac2..5a1f2d1a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3988,6 +3988,13 @@
limit is unknown. -->
<item name="config_hapticChannelMaxVibrationAmplitude" format="float" type="dimen">0</item>
+ <!-- The fixed keyboard vibration strength in [0,1], or -1 to indicate the strength not fixed
+ and should depend on the touch feedback intensity user setting -->
+ <item name="config_keyboardHapticFeedbackFixedAmplitude" format="float" type="dimen">-1</item>
+
+ <!-- The default value for keyboard vibration toggle in settings. -->
+ <bool name="config_defaultKeyboardVibrationEnabled">true</bool>
+
<!-- If the device should still vibrate even in low power mode, for certain priority vibrations
(e.g. accessibility, alarms). This is mainly for Wear devices that don't have speakers. -->
<bool name="config_allowPriorityVibrationsInLowPowerMode">false</bool>
@@ -5541,6 +5548,14 @@
<!-- Add packages here -->
</string-array>
+ <!-- Enable pause wallpaper rendering upon state change such as app launch -->
+ <bool name="config_pauseWallpaperRenderWhenStateChangeEnabled">false</bool>
+
+ <!-- The list of packages to pause wallpaper rendering upon state change such as app launch -->
+ <string-array name="pause_wallpaper_render_when_state_change" translatable="false">
+ <!-- Add packages here -->
+ </string-array>
+
<!-- Whether or not to hide the navigation bar when the soft keyboard is visible in order to
create additional screen real estate outside beyond the keyboard. Note that the user needs
to have a confirmed way to dismiss the keyboard when desired. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 76744ea..8e1c09e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2053,6 +2053,8 @@
<java-symbol type="integer" name="config_previousVibrationsDumpAggregationTimeMillisLimit" />
<java-symbol type="integer" name="config_defaultVibrationAmplitude" />
<java-symbol type="dimen" name="config_hapticChannelMaxVibrationAmplitude" />
+ <java-symbol type="dimen" name="config_keyboardHapticFeedbackFixedAmplitude" />
+ <java-symbol type="bool" name="config_defaultKeyboardVibrationEnabled" />
<java-symbol type="integer" name="config_vibrationWaveformRampStepDuration" />
<java-symbol type="bool" name="config_ignoreVibrationsOnWirelessCharger" />
<java-symbol type="integer" name="config_vibrationWaveformRampDownDuration" />
@@ -4306,6 +4308,8 @@
<java-symbol type="string" name="config_factoryResetPackage" />
<java-symbol type="array" name="config_highRefreshRateBlacklist" />
<java-symbol type="array" name="config_forceSlowJpegModeList" />
+ <java-symbol type="array" name="pause_wallpaper_render_when_state_change" />
+ <java-symbol type="bool" name="config_pauseWallpaperRenderWhenStateChangeEnabled" />
<java-symbol type="array" name="config_smallAreaDetectionAllowlist" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index af8c69e..3a2e50a 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -54,6 +54,9 @@
<!-- Azerbaijan: 4-5 digits, known premium codes listed -->
<shortcode country="az" pattern="\\d{4,5}" premium="330[12]|87744|901[234]|93(?:94|101)|9426|9525" />
+ <!-- Bangladesh: 1-5 digits (standard system default, not country specific) -->
+ <shortcode country="bd" pattern="\\d{1,5}" free="16672" />
+
<!-- Belgium: 4 digits, plus EU: http://www.mobileweb.be/en/mobileweb/sms-numberplan.asp -->
<shortcode country="be" premium="\\d{4}" free="8\\d{3}|116\\d{3}" />
@@ -145,7 +148,7 @@
<shortcode country="in" pattern="\\d{1,5}" free="59336|53969" />
<!-- Indonesia: 1-5 digits (standard system default, not country specific) -->
- <shortcode country="id" pattern="\\d{1,5}" free="99477|6006|46645|363" />
+ <shortcode country="id" pattern="\\d{1,5}" free="99477|6006|46645|363|93457" />
<!-- Ireland: 5 digits, 5xxxx (50xxx=free, 5[12]xxx=standard), plus EU:
http://www.comreg.ie/_fileupload/publications/ComReg1117.pdf -->
@@ -190,7 +193,7 @@
<shortcode country="mk" pattern="\\d{1,6}" free="129005|122" />
<!-- Mexico: 4-5 digits (not confirmed), known premium codes listed -->
- <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963|91101" />
+ <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963|91101|45453" />
<!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf -->
<shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288|66668" />
@@ -205,7 +208,7 @@
<shortcode country="no" pattern="\\d{4,5}" premium="2201|222[67]" free="2171" />
<!-- New Zealand: 3-4 digits, known premium codes listed -->
- <shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995|4679" free="1737|176|2141|3067|3068|3110|4006|4053|4061|4062|4202|4300|4334|4412|4575|5626|8006|8681" />
+ <shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995|4679" free="1737|176|2141|3067|3068|3110|3876|4006|4053|4061|4062|4202|4300|4334|4412|4575|5626|8006|8681" />
<!-- Peru: 4-5 digits (not confirmed), known premium codes listed -->
<shortcode country="pe" pattern="\\d{4,5}" free="9963|40778" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 2993a0e..445ddf5 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -65,6 +65,7 @@
"device-time-shell-utils",
"testables",
"com.android.text.flags-aconfig-java",
+ "flag-junit",
],
libs: [
@@ -75,6 +76,7 @@
"framework",
"ext",
"framework-res",
+ "android.view.flags-aconfig-java",
],
jni_libs: [
"libpowermanagertest_jni",
diff --git a/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java b/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
index 07dec5d..b843ad7 100644
--- a/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
@@ -79,6 +79,8 @@
private FaceManager.AuthenticationCallback mAuthCallback;
@Mock
private FaceManager.EnrollmentCallback mEnrollmentCallback;
+ @Mock
+ private FaceManager.FaceDetectionCallback mFaceDetectionCallback;
@Captor
private ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mCaptor;
@@ -191,6 +193,23 @@
any(), anyString(), any(), any(), anyBoolean());
}
+ @Test
+ public void detectClient_onError() throws RemoteException {
+ ArgumentCaptor<IFaceServiceReceiver> argumentCaptor =
+ ArgumentCaptor.forClass(IFaceServiceReceiver.class);
+
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ mFaceManager.detectFace(cancellationSignal, mFaceDetectionCallback,
+ new FaceAuthenticateOptions.Builder().build());
+
+ verify(mService).detectFace(any(), argumentCaptor.capture(), any());
+
+ argumentCaptor.getValue().onError(5 /* error */, 0 /* vendorCode */);
+ mLooper.dispatchAll();
+
+ verify(mFaceDetectionCallback).onDetectionError(anyInt());
+ }
+
private void initializeProperties() throws RemoteException {
verify(mService).addAuthenticatorsRegisteredCallback(mCaptor.capture());
diff --git a/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java
index 625e2e3..70313b8 100644
--- a/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java
@@ -74,6 +74,8 @@
private FingerprintManager.AuthenticationCallback mAuthCallback;
@Mock
private FingerprintManager.EnrollmentCallback mEnrollCallback;
+ @Mock
+ private FingerprintManager.FingerprintDetectionCallback mFingerprintDetectionCallback;
@Captor
private ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mCaptor;
@@ -166,4 +168,21 @@
anyString());
verify(mService, never()).enroll(any(), any(), anyInt(), any(), anyString(), anyInt());
}
+
+ @Test
+ public void detectClient_onError() throws RemoteException {
+ ArgumentCaptor<IFingerprintServiceReceiver> argumentCaptor =
+ ArgumentCaptor.forClass(IFingerprintServiceReceiver.class);
+
+ mFingerprintManager.detectFingerprint(new CancellationSignal(),
+ mFingerprintDetectionCallback,
+ new FingerprintAuthenticateOptions.Builder().build());
+
+ verify(mService).detectFingerprint(any(), argumentCaptor.capture(), any());
+
+ argumentCaptor.getValue().onError(5 /* error */, 0 /* vendorCode */);
+ mLooper.dispatchAll();
+
+ verify(mFingerprintDetectionCallback).onDetectionError(anyInt());
+ }
}
diff --git a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
index 517aeae..0855268 100644
--- a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
+++ b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
@@ -20,8 +20,6 @@
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
-import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM;
-
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -42,16 +40,12 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.SharedMemory;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.TestableContext;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.Flag;
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.FlagResolver;
-
-import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
@@ -71,6 +65,9 @@
private NotificationChannel mNotificationChannel;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
// TODO(b/284297289): remove this flag set once resolved.
@Parameterized.Parameters(name = "rankingUpdateAshmem={0}")
public static Boolean[] getRankingUpdateAshmem() {
@@ -424,30 +421,11 @@
mNotificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "test channel",
NotificationManager.IMPORTANCE_DEFAULT);
- SystemUiSystemPropertiesFlags.TEST_RESOLVER = new FlagResolver() {
- @Override
- public boolean isEnabled(Flag flag) {
- if (flag.mSysPropKey.equals(RANKING_UPDATE_ASHMEM.mSysPropKey)) {
- return mRankingUpdateAshmem;
- }
- return new SystemUiSystemPropertiesFlags.DebugResolver().isEnabled(flag);
- }
-
- @Override
- public int getIntValue(Flag flag) {
- return 0;
- }
-
- @Override
- public String getStringValue(Flag flag) {
- return null;
- }
- };
- }
-
- @After
- public void tearDown() {
- SystemUiSystemPropertiesFlags.TEST_RESOLVER = null;
+ if (mRankingUpdateAshmem) {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RANKING_UPDATE_ASHMEM);
+ } else {
+ mSetFlagsRule.disableFlags(Flags.FLAG_RANKING_UPDATE_ASHMEM);
+ }
}
/**
@@ -497,8 +475,7 @@
parcel.setDataPosition(0);
NotificationRankingUpdate nru1 = NotificationRankingUpdate.CREATOR.createFromParcel(parcel);
// The rankingUpdate file descriptor is only non-null in the new path.
- if (SystemUiSystemPropertiesFlags.getResolver().isEnabled(
- SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) {
+ if (Flags.rankingUpdateAshmem()) {
assertTrue(nru1.isFdNotNullAndClosed());
}
detailedAssertEquals(nru, nru1);
@@ -636,7 +613,7 @@
@Test
public void testRankingUpdate_writesSmartActionToParcel() {
- if (!mRankingUpdateAshmem) {
+ if (!Flags.rankingUpdateAshmem()) {
return;
}
ArrayList<Notification.Action> actions = new ArrayList<>();
@@ -674,7 +651,7 @@
@Test
public void testRankingUpdate_handlesEmptySmartActionList() {
- if (!mRankingUpdateAshmem) {
+ if (!Flags.rankingUpdateAshmem()) {
return;
}
ArrayList<Notification.Action> actions = new ArrayList<>();
@@ -697,7 +674,7 @@
@Test
public void testRankingUpdate_handlesNullSmartActionList() {
- if (!mRankingUpdateAshmem) {
+ if (!Flags.rankingUpdateAshmem()) {
return;
}
NotificationListenerService.Ranking ranking =
diff --git a/core/tests/coretests/src/android/view/OWNERS b/core/tests/coretests/src/android/view/OWNERS
index 2ca9994..23668a4 100644
--- a/core/tests/coretests/src/android/view/OWNERS
+++ b/core/tests/coretests/src/android/view/OWNERS
@@ -20,4 +20,7 @@
per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
# Stylus
-per-file stylus/* = file:/core/java/android/text/OWNERS
\ No newline at end of file
+per-file stylus/* = file:/core/java/android/text/OWNERS
+
+# View
+file:/core/java/android/view/OWNERS
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 6a9fc04..1a38dec 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -17,6 +17,11 @@
package android.view;
import static android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR;
+import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE;
+import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
+import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
+import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
+import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
@@ -48,8 +53,12 @@
import android.os.Binder;
import android.os.SystemProperties;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Type;
@@ -97,6 +106,10 @@
// state after the test completes.
private static boolean sOriginalTouchMode;
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
@BeforeClass
public static void setUpClass() {
sContext = sInstrumentation.getTargetContext();
@@ -427,6 +440,129 @@
assertThat(result).isFalse();
}
+ /**
+ * Test the default values are properly set
+ */
+ @UiThreadTest
+ @Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE)
+ public void votePreferredFrameRate_getDefaultValues() {
+ ViewRootImpl viewRootImpl = new ViewRootImpl(sContext,
+ sContext.getDisplayNoVerify());
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
+ FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
+ }
+
+ /**
+ * Test the value of the frame rate cateogry based on the visibility of a view
+ * Invsible: FRAME_RATE_CATEGORY_NO_PREFERENCE
+ * Visible: FRAME_RATE_CATEGORY_NORMAL
+ */
+ @Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE)
+ public void votePreferredFrameRate_voteFrameRateCategory_visibility() {
+ View view = new View(sContext);
+ attachViewToWindow(view);
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ sInstrumentation.runOnMainSync(() -> {
+ view.setVisibility(View.INVISIBLE);
+ view.invalidate();
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
+ FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ });
+
+ sInstrumentation.runOnMainSync(() -> {
+ view.setVisibility(View.VISIBLE);
+ view.invalidate();
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
+ FRAME_RATE_CATEGORY_NORMAL);
+ });
+ }
+
+ /**
+ * Test the value of the frame rate cateogry based on the size of a view.
+ * The current threshold value is 7% of the screen size
+ * <7%: FRAME_RATE_CATEGORY_LOW
+ */
+ @Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE)
+ public void votePreferredFrameRate_voteFrameRateCategory_smallSize() {
+ View view = new View(sContext);
+ WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
+ wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
+ wmlp.width = 1;
+ wmlp.height = 1;
+
+ sInstrumentation.runOnMainSync(() -> {
+ WindowManager wm = sContext.getSystemService(WindowManager.class);
+ wm.addView(view, wmlp);
+ });
+ sInstrumentation.waitForIdleSync();
+
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ sInstrumentation.runOnMainSync(() -> {
+ view.invalidate();
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
+ });
+ }
+
+ /**
+ * Test the value of the frame rate cateogry based on the size of a view.
+ * The current threshold value is 7% of the screen size
+ * >=7% : FRAME_RATE_CATEGORY_NORMAL
+ */
+ @Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE)
+ public void votePreferredFrameRate_voteFrameRateCategory_normalSize() {
+ View view = new View(sContext);
+ WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
+ wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
+
+ sInstrumentation.runOnMainSync(() -> {
+ WindowManager wm = sContext.getSystemService(WindowManager.class);
+ Display display = wm.getDefaultDisplay();
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getMetrics(metrics);
+ wmlp.width = (int) (metrics.widthPixels * 0.9);
+ wmlp.height = (int) (metrics.heightPixels * 0.9);
+ wm.addView(view, wmlp);
+ });
+ sInstrumentation.waitForIdleSync();
+
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ sInstrumentation.runOnMainSync(() -> {
+ view.invalidate();
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
+ });
+ }
+
+ /**
+ * Test how values of the frame rate cateogry are aggregated.
+ * It should take the max value among all of the voted categories per frame.
+ */
+ @Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE)
+ public void votePreferredFrameRate_voteFrameRateCategory_aggregate() {
+ View view = new View(sContext);
+ attachViewToWindow(view);
+ sInstrumentation.runOnMainSync(() -> {
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
+ FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_LOW);
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
+ viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_NORMAL);
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
+ viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_HIGH);
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
+ viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_NORMAL);
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
+ viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_LOW);
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
+ });
+ }
+
@Test
public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() {
mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
diff --git a/core/tests/coretests/src/android/widget/HorizontalScrollViewFunctionalTest.java b/core/tests/coretests/src/android/widget/HorizontalScrollViewFunctionalTest.java
index 86f26e5..df212eb 100644
--- a/core/tests/coretests/src/android/widget/HorizontalScrollViewFunctionalTest.java
+++ b/core/tests/coretests/src/android/widget/HorizontalScrollViewFunctionalTest.java
@@ -17,7 +17,9 @@
package android.widget;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import android.content.Context;
import android.platform.test.annotations.Presubmit;
import android.util.PollingCheck;
@@ -32,6 +34,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
@RunWith(AndroidJUnit4.class)
@MediumTest
@Presubmit
@@ -49,23 +54,43 @@
}
@Test
- public void testScrollAfterFlingTop() {
- mHorizontalScrollView.scrollTo(100, 0);
- mHorizontalScrollView.fling(-10000);
- PollingCheck.waitFor(() -> mHorizontalScrollView.mEdgeGlowLeft.getDistance() > 0);
- PollingCheck.waitFor(() -> mHorizontalScrollView.mEdgeGlowLeft.getDistance() == 0f);
+ public void testScrollAfterFlingLeft() throws Throwable {
+ WatchedEdgeEffect edgeEffect = new WatchedEdgeEffect(mActivity);
+ mHorizontalScrollView.mEdgeGlowLeft = edgeEffect;
+ mActivityRule.runOnUiThread(() -> mHorizontalScrollView.scrollTo(100, 0));
+ mActivityRule.runOnUiThread(() -> mHorizontalScrollView.fling(-10000));
+ assertTrue(edgeEffect.onAbsorbLatch.await(1, TimeUnit.SECONDS));
+ mActivityRule.runOnUiThread(() -> {}); // let the absorb takes effect -- least one frame
+ PollingCheck.waitFor(() -> edgeEffect.getDistance() == 0f);
assertEquals(0, mHorizontalScrollView.getScrollX());
}
@Test
- public void testScrollAfterFlingBottom() {
+ public void testScrollAfterFlingRight() throws Throwable {
+ WatchedEdgeEffect edgeEffect = new WatchedEdgeEffect(mActivity);
+ mHorizontalScrollView.mEdgeGlowRight = edgeEffect;
int childWidth = mHorizontalScrollView.getChildAt(0).getWidth();
int maxScroll = childWidth - mHorizontalScrollView.getWidth();
- mHorizontalScrollView.scrollTo(maxScroll - 100, 0);
- mHorizontalScrollView.fling(10000);
- PollingCheck.waitFor(() -> mHorizontalScrollView.mEdgeGlowRight.getDistance() > 0);
+ mActivityRule.runOnUiThread(() -> mHorizontalScrollView.scrollTo(maxScroll - 100, 0));
+ mActivityRule.runOnUiThread(() -> mHorizontalScrollView.fling(10000));
+ assertTrue(edgeEffect.onAbsorbLatch.await(1, TimeUnit.SECONDS));
+ mActivityRule.runOnUiThread(() -> {}); // let the absorb takes effect -- at least one frame
PollingCheck.waitFor(() -> mHorizontalScrollView.mEdgeGlowRight.getDistance() == 0f);
assertEquals(maxScroll, mHorizontalScrollView.getScrollX());
}
+
+ static class WatchedEdgeEffect extends EdgeEffect {
+ public CountDownLatch onAbsorbLatch = new CountDownLatch(1);
+
+ WatchedEdgeEffect(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onAbsorb(int velocity) {
+ super.onAbsorb(velocity);
+ onAbsorbLatch.countDown();
+ }
+ }
}
diff --git a/graphics/java/android/framework_graphics.aconfig b/graphics/java/android/framework_graphics.aconfig
index e030dad..9a0a22a 100644
--- a/graphics/java/android/framework_graphics.aconfig
+++ b/graphics/java/android/framework_graphics.aconfig
@@ -2,7 +2,7 @@
flag {
name: "exact_compute_bounds"
- namespace: "framework_graphics"
+ namespace: "core_graphics"
description: "Add a function without unused exact param for computeBounds."
bug: "304478551"
}
\ No newline at end of file
diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java
index f5e5803..dc1773b 100644
--- a/graphics/java/android/graphics/text/LineBreakConfig.java
+++ b/graphics/java/android/graphics/text/LineBreakConfig.java
@@ -39,12 +39,14 @@
/**
* No hyphenation preference is specified.
*
+ * <p>
* This is a special value of hyphenation preference indicating no hyphenation preference is
* specified. When overriding a {@link LineBreakConfig} with another {@link LineBreakConfig}
* with {@link Builder#merge(LineBreakConfig)} function, the hyphenation preference of
* overridden config will be kept if the hyphenation preference of overriding config is
* {@link #HYPHENATION_UNSPECIFIED}.
*
+ * <p>
* <pre>
* val override = LineBreakConfig.Builder()
* .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
@@ -57,6 +59,7 @@
* // LINE_BREAK_WORD_STYLE_PHRASE for line break word style.
* </pre>
*
+ * <p>
* This value is resolved to {@link #HYPHENATION_ENABLED} if this value is used for text
* layout/rendering.
*/
@@ -89,6 +92,7 @@
/**
* No line break style is specified.
*
+ * <p>
* This is a special value of line break style indicating no style value is specified.
* When overriding a {@link LineBreakConfig} with another {@link LineBreakConfig} with
* {@link Builder#merge(LineBreakConfig)} function, the line break style of overridden config
@@ -107,6 +111,7 @@
* // LINE_BREAK_WORD_STYLE_PHRASE for line break word style.
* </pre>
*
+ * <p>
* This value is resolved to {@link #LINE_BREAK_STYLE_NONE} if this value is used for text
* layout/rendering.
*/
@@ -270,6 +275,8 @@
/**
* Resets this builder to the given config state.
*
+ * @param config a config value used for resetting. {@code null} is allowed. If {@code null}
+ * is passed, all configs are reset to unspecified.
* @return This {@code Builder}.
* @hide
*/
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index c72a42c..18796494 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -151,6 +151,7 @@
static_libs: [
"androidx.appcompat_appcompat",
"androidx.core_core-animation",
+ "androidx.core_core-ktx",
"androidx.arch.core_core-runtime",
"androidx-constraintlayout_constraintlayout",
"androidx.dynamicanimation_dynamicanimation",
diff --git a/libs/WindowManager/Shell/res/values-television/config.xml b/libs/WindowManager/Shell/res/values-television/config.xml
index da8abde..8d2e28b 100644
--- a/libs/WindowManager/Shell/res/values-television/config.xml
+++ b/libs/WindowManager/Shell/res/values-television/config.xml
@@ -45,13 +45,13 @@
<integer name="config_pipForceCloseDelay">5000</integer>
<!-- Animation duration when exit starting window: fade out icon -->
- <integer name="starting_window_app_reveal_icon_fade_out_duration">500</integer>
+ <integer name="starting_window_app_reveal_icon_fade_out_duration">200</integer>
<!-- Animation delay when exit starting window: reveal app -->
- <integer name="starting_window_app_reveal_anim_delay">0</integer>
+ <integer name="starting_window_app_reveal_anim_delay">200</integer>
<!-- Animation duration when exit starting window: reveal app -->
- <integer name="starting_window_app_reveal_anim_duration">500</integer>
+ <integer name="starting_window_app_reveal_anim_duration">300</integer>
<!-- Default animation type when hiding the starting window. The possible values are:
- 0 for radial vanish + slide up
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 2241c34..ac5ba51e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1784,13 +1784,14 @@
mStackOnLeftOrWillBe = mPositioner.isStackOnLeft(startPosition);
mStackAnimationController.setStackPosition(startPosition);
mExpandedAnimationController.setCollapsePoint(startPosition);
- // Set the translation x so that this bubble will animate in from the same side they
- // expand / collapse on.
- bubble.getIconView().setTranslationX(startPosition.x);
} else if (firstBubble) {
mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
}
+ // Set the view translation x so that this bubble will animate in from the same side they
+ // expand / collapse on.
+ bubble.getIconView().setTranslationX(mStackAnimationController.getStackPosition().x);
+
mBubbleContainer.addView(bubble.getIconView(), 0,
new FrameLayout.LayoutParams(mPositioner.getBubbleSize(),
mPositioner.getBubbleSize()));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index c51af46..ea7b2e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -605,6 +605,7 @@
@Provides
static Transitions provideTransitions(Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ShellController shellController,
ShellTaskOrganizer organizer,
TransactionPool pool,
@@ -612,14 +613,13 @@
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler,
@ShellAnimationThread ShellExecutor animExecutor,
- ShellCommandHandler shellCommandHandler,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
if (!context.getResources().getBoolean(R.bool.config_registerShellTransitionsOnInit)) {
// TODO(b/238217847): Force override shell init if registration is disabled
shellInit = new ShellInit(mainExecutor);
}
- return new Transitions(context, shellInit, shellController, organizer, pool,
- displayController, mainExecutor, mainHandler, animExecutor, shellCommandHandler,
+ return new Transitions(context, shellInit, shellCommandHandler, shellController, organizer,
+ pool, displayController, mainExecutor, mainHandler, animExecutor,
rootTaskDisplayAreaOrganizer);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/OWNERS
new file mode 100644
index 0000000..74a29dd
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/OWNERS
@@ -0,0 +1 @@
+hwwang@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 8a64037..1898ea7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -17,19 +17,28 @@
package com.android.wm.shell.dagger.pip;
import android.annotation.NonNull;
+import android.content.Context;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
+import com.android.wm.shell.pip2.phone.PipController;
import com.android.wm.shell.pip2.phone.PipTransition;
+import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import dagger.Module;
import dagger.Provides;
+import java.util.Optional;
+
/**
* Provides dependencies from {@link com.android.wm.shell.pip2}, this implementation is meant to be
* the successor of its sibling {@link Pip1Module}.
@@ -42,8 +51,26 @@
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@NonNull Transitions transitions,
PipBoundsState pipBoundsState,
- PipBoundsAlgorithm pipBoundsAlgorithm) {
+ PipBoundsAlgorithm pipBoundsAlgorithm,
+ Optional<PipController> pipController) {
return new PipTransition(shellInit, shellTaskOrganizer, transitions, pipBoundsState, null,
pipBoundsAlgorithm);
}
+
+ @WMSingleton
+ @Provides
+ static Optional<PipController> providePipController(Context context,
+ ShellInit shellInit,
+ ShellController shellController,
+ DisplayController displayController,
+ DisplayInsetsController displayInsetsController,
+ PipDisplayLayoutState pipDisplayLayoutState) {
+ if (!PipUtils.isPip2ExperimentEnabled()) {
+ return Optional.empty();
+ } else {
+ return Optional.ofNullable(PipController.create(
+ context, shellInit, shellController, displayController, displayInsetsController,
+ pipDisplayLayoutState));
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
new file mode 100644
index 0000000..186cb61
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip2.phone;
+
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.view.InsetsState;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipUtils;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.sysui.ConfigurationChangeListener;
+import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
+
+/**
+ * Manages the picture-in-picture (PIP) UI and states for Phones.
+ */
+public class PipController implements ConfigurationChangeListener,
+ DisplayController.OnDisplaysChangedListener {
+ private static final String TAG = PipController.class.getSimpleName();
+
+ private Context mContext;
+ private ShellController mShellController;
+ private DisplayController mDisplayController;
+ private DisplayInsetsController mDisplayInsetsController;
+ private PipDisplayLayoutState mPipDisplayLayoutState;
+
+ private PipController(Context context,
+ ShellInit shellInit,
+ ShellController shellController,
+ DisplayController displayController,
+ DisplayInsetsController displayInsetsController,
+ PipDisplayLayoutState pipDisplayLayoutState) {
+ mContext = context;
+ mShellController = shellController;
+ mDisplayController = displayController;
+ mDisplayInsetsController = displayInsetsController;
+ mPipDisplayLayoutState = pipDisplayLayoutState;
+
+ if (PipUtils.isPip2ExperimentEnabled()) {
+ shellInit.addInitCallback(this::onInit, this);
+ }
+ }
+
+ private void onInit() {
+ // Ensure that we have the display info in case we get calls to update the bounds before the
+ // listener calls back
+ mPipDisplayLayoutState.setDisplayId(mContext.getDisplayId());
+ DisplayLayout layout = new DisplayLayout(mContext, mContext.getDisplay());
+ mPipDisplayLayoutState.setDisplayLayout(layout);
+
+ mShellController.addConfigurationChangeListener(this);
+ mDisplayController.addDisplayWindowListener(this);
+ mDisplayInsetsController.addInsetsChangedListener(mPipDisplayLayoutState.getDisplayId(),
+ new DisplayInsetsController.OnInsetsChangedListener() {
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ onDisplayChanged(mDisplayController
+ .getDisplayLayout(mPipDisplayLayoutState.getDisplayId()));
+ }
+ });
+ }
+
+ /**
+ * Instantiates {@link PipController}, returns {@code null} if the feature not supported.
+ */
+ public static PipController create(Context context,
+ ShellInit shellInit,
+ ShellController shellController,
+ DisplayController displayController,
+ DisplayInsetsController displayInsetsController,
+ PipDisplayLayoutState pipDisplayLayoutState) {
+ if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
+ ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Device doesn't support Pip feature", TAG);
+ return null;
+ }
+ return new PipController(context, shellInit, shellController, displayController,
+ displayInsetsController, pipDisplayLayoutState);
+ }
+
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfiguration) {
+ mPipDisplayLayoutState.onConfigurationChanged();
+ }
+
+ @Override
+ public void onThemeChanged() {
+ onDisplayChanged(new DisplayLayout(mContext, mContext.getDisplay()));
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ if (displayId != mPipDisplayLayoutState.getDisplayId()) {
+ return;
+ }
+ onDisplayChanged(mDisplayController.getDisplayLayout(displayId));
+ }
+
+ @Override
+ public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ if (displayId != mPipDisplayLayoutState.getDisplayId()) {
+ return;
+ }
+ onDisplayChanged(mDisplayController.getDisplayLayout(displayId));
+ }
+
+ private void onDisplayChanged(DisplayLayout layout) {
+ mPipDisplayLayoutState.setDisplayLayout(layout);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index 8b050e5..b1fc16d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -63,7 +63,7 @@
@NonNull Transitions.TransitionFinishCallback finishCallback) {
if (mTransition != transition) return false;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Using registered One-shot remote"
- + " transition %s for #%d.", mRemote, info.getDebugId());
+ + " transition %s for (#%d).", mRemote, info.getDebugId());
final IBinder.DeathRecipient remoteDied = () -> {
Log.e(Transitions.TAG, "Remote transition died, finishing");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index 592b22a..ca2c3b4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -126,7 +126,7 @@
}
}
}
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Delegate animation for #%d to %s",
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Delegate animation for (#%d) to %s",
info.getDebugId(), pendingRemote);
if (pendingRemote == null) return false;
@@ -241,7 +241,7 @@
if (remote == null) return null;
mRequestedRemotes.put(transition, remote);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "RemoteTransition directly requested"
- + " for %s: %s", transition, remote);
+ + " for (#%d) %s: %s", request.getDebugId(), transition, remote);
return new WindowContainerTransaction();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 0d9a9e9..576bba96 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -179,6 +179,7 @@
private final DefaultTransitionHandler mDefaultTransitionHandler;
private final RemoteTransitionHandler mRemoteTransitionHandler;
private final DisplayController mDisplayController;
+ private final ShellCommandHandler mShellCommandHandler;
private final ShellController mShellController;
private final ShellTransitionImpl mImpl = new ShellTransitionImpl();
private final SleepHandler mSleepHandler = new SleepHandler();
@@ -188,9 +189,6 @@
/** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>();
- @Nullable
- private final ShellCommandHandler mShellCommandHandler;
-
private final ArrayList<TransitionObserver> mObservers = new ArrayList<>();
/** List of {@link Runnable} instances to run when the last active transition has finished. */
@@ -237,7 +235,7 @@
@Override
public String toString() {
if (mInfo != null && mInfo.getDebugId() >= 0) {
- return "(#" + mInfo.getDebugId() + ")" + mToken + "@" + getTrack();
+ return "(#" + mInfo.getDebugId() + ") " + mToken + "@" + getTrack();
}
return mToken.toString() + "@" + getTrack();
}
@@ -275,13 +273,14 @@
@NonNull ShellExecutor mainExecutor,
@NonNull Handler mainHandler,
@NonNull ShellExecutor animExecutor) {
- this(context, shellInit, shellController, organizer, pool, displayController, mainExecutor,
- mainHandler, animExecutor, null,
+ this(context, shellInit, new ShellCommandHandler(), shellController, organizer, pool,
+ displayController, mainExecutor, mainHandler, animExecutor,
new RootTaskDisplayAreaOrganizer(mainExecutor, context, shellInit));
}
public Transitions(@NonNull Context context,
@NonNull ShellInit shellInit,
+ @Nullable ShellCommandHandler shellCommandHandler,
@NonNull ShellController shellController,
@NonNull WindowOrganizer organizer,
@NonNull TransactionPool pool,
@@ -289,7 +288,6 @@
@NonNull ShellExecutor mainExecutor,
@NonNull Handler mainHandler,
@NonNull ShellExecutor animExecutor,
- @Nullable ShellCommandHandler shellCommandHandler,
@NonNull RootTaskDisplayAreaOrganizer rootTDAOrganizer) {
mOrganizer = organizer;
mContext = context;
@@ -300,13 +298,13 @@
mDefaultTransitionHandler = new DefaultTransitionHandler(context, shellInit,
displayController, pool, mainExecutor, mainHandler, animExecutor, rootTDAOrganizer);
mRemoteTransitionHandler = new RemoteTransitionHandler(mMainExecutor);
+ mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
// The very last handler (0 in the list) should be the default one.
mHandlers.add(mDefaultTransitionHandler);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Default");
// Next lowest priority is remote transitions.
mHandlers.add(mRemoteTransitionHandler);
- mShellCommandHandler = shellCommandHandler;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Remote");
shellInit.addInitCallback(this::onInit, this);
}
@@ -339,9 +337,8 @@
TransitionMetrics.getInstance();
}
- if (mShellCommandHandler != null) {
- mShellCommandHandler.addCommandCallback("transitions", this, this);
- }
+ mShellCommandHandler.addCommandCallback("transitions", this, this);
+ mShellCommandHandler.addDumpCallback(this::dump, this);
}
public boolean isRegistered() {
@@ -655,8 +652,8 @@
void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
info.setUnreleasedWarningCallSiteForAllSurfaces("Transitions.onTransitionReady");
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s",
- transitionToken, info);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady (#%d) %s: %s",
+ info.getDebugId(), transitionToken, info);
final int activeIdx = findByToken(mPendingTransitions, transitionToken);
if (activeIdx < 0) {
throw new IllegalStateException("Got transitionReady for non-pending transition "
@@ -1073,8 +1070,8 @@
void requestStartTransition(@NonNull IBinder transitionToken,
@Nullable TransitionRequestInfo request) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested: %s %s",
- transitionToken, request);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested (#%d): %s %s",
+ request.getDebugId(), transitionToken, request);
if (isTransitionKnown(transitionToken)) {
throw new RuntimeException("Transition already started " + transitionToken);
}
@@ -1475,4 +1472,68 @@
pw.println(prefix + "tracing");
mTracer.printShellCommandHelp(pw, prefix + " ");
}
+
+ private void dump(@NonNull PrintWriter pw, String prefix) {
+ pw.println(prefix + TAG);
+
+ final String innerPrefix = prefix + " ";
+ pw.println(prefix + "Handlers:");
+ for (TransitionHandler handler : mHandlers) {
+ pw.print(innerPrefix);
+ pw.print(handler.getClass().getSimpleName());
+ pw.println(" (" + Integer.toHexString(System.identityHashCode(handler)) + ")");
+ }
+
+ pw.println(prefix + "Observers:");
+ for (TransitionObserver observer : mObservers) {
+ pw.print(innerPrefix);
+ pw.println(observer.getClass().getSimpleName());
+ }
+
+ pw.println(prefix + "Pending Transitions:");
+ for (ActiveTransition transition : mPendingTransitions) {
+ pw.print(innerPrefix + "token=");
+ pw.println(transition.mToken);
+ pw.print(innerPrefix + "id=");
+ pw.println(transition.mInfo != null
+ ? transition.mInfo.getDebugId()
+ : -1);
+ pw.print(innerPrefix + "handler=");
+ pw.println(transition.mHandler != null
+ ? transition.mHandler.getClass().getSimpleName()
+ : null);
+ }
+ if (mPendingTransitions.isEmpty()) {
+ pw.println(innerPrefix + "none");
+ }
+
+ pw.println(prefix + "Ready-during-sync Transitions:");
+ for (ActiveTransition transition : mReadyDuringSync) {
+ pw.print(innerPrefix + "token=");
+ pw.println(transition.mToken);
+ pw.print(innerPrefix + "id=");
+ pw.println(transition.mInfo != null
+ ? transition.mInfo.getDebugId()
+ : -1);
+ pw.print(innerPrefix + "handler=");
+ pw.println(transition.mHandler != null
+ ? transition.mHandler.getClass().getSimpleName()
+ : null);
+ }
+ if (mReadyDuringSync.isEmpty()) {
+ pw.println(innerPrefix + "none");
+ }
+
+ pw.println(prefix + "Tracks:");
+ for (int i = 0; i < mTracks.size(); i++) {
+ final ActiveTransition transition = mTracks.get(i).mActiveTransition;
+ pw.println(innerPrefix + "Track #" + i);
+ pw.print(innerPrefix + "active=");
+ pw.println(transition);
+ if (transition != null) {
+ pw.print(innerPrefix + "hander=");
+ pw.println(transition.mHandler);
+ }
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
index 389db62..dadd264 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
@@ -18,6 +18,7 @@
import android.graphics.PointF;
import android.graphics.Rect;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;
@@ -45,6 +46,7 @@
private final int mDisallowedAreaForEndBoundsHeight;
private boolean mHasDragResized;
private int mCtrlType;
+ @Surface.Rotation private int mRotation;
FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration,
DisplayController displayController, int disallowedAreaForEndBoundsHeight) {
@@ -78,7 +80,10 @@
mTaskOrganizer.applyTransaction(wct);
}
mRepositionTaskBounds.set(mTaskBoundsAtDragStart);
- if (mStableBounds.isEmpty()) {
+ int rotation = mWindowDecoration
+ .mTaskInfo.configuration.windowConfiguration.getDisplayRotation();
+ if (mStableBounds.isEmpty() || mRotation != rotation) {
+ mRotation = rotation;
mDisplayController.getDisplayLayout(mWindowDecoration.mDisplay.getDisplayId())
.getStableBounds(mStableBounds);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index 303954a..852c037 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -21,6 +21,7 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.IBinder;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
@@ -58,6 +59,7 @@
private final int mDisallowedAreaForEndBoundsHeight;
private final Supplier<SurfaceControl.Transaction> mTransactionSupplier;
private int mCtrlType;
+ @Surface.Rotation private int mRotation;
public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
DesktopModeWindowDecoration windowDecoration, DisplayController displayController,
@@ -98,7 +100,10 @@
}
mDragStartListener.onDragStart(mDesktopWindowDecoration.mTaskInfo.taskId);
mRepositionTaskBounds.set(mTaskBoundsAtDragStart);
- if (mStableBounds.isEmpty()) {
+ int rotation = mDesktopWindowDecoration
+ .mTaskInfo.configuration.windowConfiguration.getDisplayRotation();
+ if (mStableBounds.isEmpty() || mRotation != rotation) {
+ mRotation = rotation;
mDisplayController.getDisplayLayout(mDesktopWindowDecoration.mDisplay.getDisplayId())
.getStableBounds(mStableBounds);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 0548a8e..d0e647b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -297,7 +297,7 @@
}
// Task surface itself
- float shadowRadius = loadDimension(resources, params.mShadowRadiusId);
+ float shadowRadius;
final Point taskPosition = mTaskInfo.positionInParent;
if (isFullscreen) {
// Setting the task crop to the width/height stops input events from being sent to
@@ -308,9 +308,12 @@
// drag-resized by the window decoration.
startT.setWindowCrop(mTaskSurface, null);
finishT.setWindowCrop(mTaskSurface, null);
+ // Shadow is not needed for fullscreen tasks
+ shadowRadius = 0;
} else {
startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
+ shadowRadius = loadDimension(resources, params.mShadowRadiusId);
}
startT.setShadowRadius(mTaskSurface, shadowRadius)
.show(mTaskSurface);
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 7f02072..e111edc 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -39,7 +39,25 @@
}
filegroup {
- name: "WMShellFlickerTestsPip-src",
+ name: "WMShellFlickerTestsPip1-src",
+ srcs: [
+ "src/com/android/wm/shell/flicker/pip/A*.kt",
+ "src/com/android/wm/shell/flicker/pip/B*.kt",
+ "src/com/android/wm/shell/flicker/pip/C*.kt",
+ "src/com/android/wm/shell/flicker/pip/D*.kt",
+ "src/com/android/wm/shell/flicker/pip/S*.kt",
+ ],
+}
+
+filegroup {
+ name: "WMShellFlickerTestsPip2-src",
+ srcs: [
+ "src/com/android/wm/shell/flicker/pip/E*.kt",
+ ],
+}
+
+filegroup {
+ name: "WMShellFlickerTestsPip3-src",
srcs: ["src/com/android/wm/shell/flicker/pip/*.kt"],
}
@@ -176,7 +194,9 @@
],
exclude_srcs: [
":WMShellFlickerTestsBubbles-src",
- ":WMShellFlickerTestsPip-src",
+ ":WMShellFlickerTestsPip1-src",
+ ":WMShellFlickerTestsPip2-src",
+ ":WMShellFlickerTestsPip3-src",
":WMShellFlickerTestsPipCommon-src",
":WMShellFlickerTestsPipApps-src",
":WMShellFlickerTestsSplitScreenGroup1-src",
@@ -207,12 +227,55 @@
instrumentation_target_package: "com.android.wm.shell.flicker.pip",
srcs: [
":WMShellFlickerTestsBase-src",
- ":WMShellFlickerTestsPip-src",
+ ":WMShellFlickerTestsPip3-src",
":WMShellFlickerTestsPipCommon-src",
],
}
android_test {
+ name: "WMShellFlickerTestsPip1",
+ defaults: ["WMShellFlickerTestsDefault"],
+ additional_manifests: ["manifests/AndroidManifestPip.xml"],
+ package_name: "com.android.wm.shell.flicker.pip",
+ instrumentation_target_package: "com.android.wm.shell.flicker.pip",
+ srcs: [
+ ":WMShellFlickerTestsBase-src",
+ ":WMShellFlickerTestsPip1-src",
+ ":WMShellFlickerTestsPipCommon-src",
+ ],
+}
+
+android_test {
+ name: "WMShellFlickerTestsPip2",
+ defaults: ["WMShellFlickerTestsDefault"],
+ additional_manifests: ["manifests/AndroidManifestPip.xml"],
+ package_name: "com.android.wm.shell.flicker.pip",
+ instrumentation_target_package: "com.android.wm.shell.flicker.pip",
+ srcs: [
+ ":WMShellFlickerTestsBase-src",
+ ":WMShellFlickerTestsPip2-src",
+ ":WMShellFlickerTestsPipCommon-src",
+ ],
+}
+
+android_test {
+ name: "WMShellFlickerTestsPip3",
+ defaults: ["WMShellFlickerTestsDefault"],
+ additional_manifests: ["manifests/AndroidManifestPip.xml"],
+ package_name: "com.android.wm.shell.flicker.pip",
+ instrumentation_target_package: "com.android.wm.shell.flicker.pip",
+ srcs: [
+ ":WMShellFlickerTestsBase-src",
+ ":WMShellFlickerTestsPip3-src",
+ ":WMShellFlickerTestsPipCommon-src",
+ ],
+ exclude_srcs: [
+ ":WMShellFlickerTestsPip1-src",
+ ":WMShellFlickerTestsPip2-src",
+ ],
+}
+
+android_test {
name: "WMShellFlickerTestsPipApps",
defaults: ["WMShellFlickerTestsDefault"],
additional_manifests: ["manifests/AndroidManifestPip.xml"],
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index 19c8435..94e3959 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -22,6 +22,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.pip.common.EnterPipTransition
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -55,7 +56,7 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) :
- EnterPipViaAppUiButtonTest(flicker) {
+ EnterPipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } }
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
index c0c4498..add78b2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
@@ -6,6 +6,9 @@
import android.os.IBinder
import android.testing.AndroidTestingRunner
import android.view.Display
+import android.view.Surface
+import android.view.Surface.ROTATION_270
+import android.view.Surface.ROTATION_90
import android.view.SurfaceControl
import android.window.WindowContainerToken
import android.window.WindowContainerTransaction
@@ -24,6 +27,7 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
+import org.mockito.Mockito
import org.mockito.Mockito.any
import org.mockito.Mockito.argThat
import org.mockito.Mockito.never
@@ -76,7 +80,15 @@
whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
whenever(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)
whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
- (i.arguments.first() as Rect).set(STABLE_BOUNDS)
+ if (mockWindowDecoration.mTaskInfo.configuration.windowConfiguration
+ .displayRotation == ROTATION_90 ||
+ mockWindowDecoration.mTaskInfo.configuration.windowConfiguration
+ .displayRotation == ROTATION_270
+ ) {
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS_LANDSCAPE)
+ } else {
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS_PORTRAIT)
+ }
}
`when`(mockDisplayLayout.stableInsets()).thenReturn(STABLE_INSETS)
`when`(mockTransactionFactory.get()).thenReturn(mockTransaction)
@@ -89,6 +101,7 @@
defaultMinSize = DEFAULT_MIN
displayId = DISPLAY_ID
configuration.windowConfiguration.setBounds(STARTING_BOUNDS)
+ configuration.windowConfiguration.displayRotation = ROTATION_90
}
mockWindowDecoration.mDisplay = mockDisplay
whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
@@ -623,7 +636,7 @@
)
val newX = STARTING_BOUNDS.left.toFloat()
- val newY = STABLE_BOUNDS.top.toFloat() - 5
+ val newY = STABLE_BOUNDS_LANDSCAPE.top.toFloat() - 5
taskPositioner.onDragPositioningMove(
newX,
newY
@@ -641,11 +654,83 @@
token == taskBinder &&
(change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
change.configuration.windowConfiguration.bounds.top ==
- STABLE_BOUNDS.top
+ STABLE_BOUNDS_LANDSCAPE.top
}
})
}
+ @Test
+ fun testDragResize_drag_updatesStableBoundsOnRotate() {
+ // Test landscape stable bounds
+ performDrag(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(),
+ STARTING_BOUNDS.right.toFloat() + 2000, STARTING_BOUNDS.bottom.toFloat() + 2000,
+ CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM)
+ val rectAfterDrag = Rect(STARTING_BOUNDS)
+ rectAfterDrag.right += 2000
+ // First drag; we should fetch stable bounds.
+ verify(mockDisplayLayout, Mockito.times(1)).getStableBounds(any())
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.configuration.windowConfiguration.bounds == rectAfterDrag
+ }
+ })
+ // Drag back to starting bounds.
+ performDrag(
+ STARTING_BOUNDS.right.toFloat() + 2000, STARTING_BOUNDS.bottom.toFloat(),
+ STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(),
+ CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM)
+
+ // Display did not rotate; we should use previous stable bounds
+ verify(mockDisplayLayout, Mockito.times(1)).getStableBounds(any())
+
+ // Rotate the screen to portrait
+ mockWindowDecoration.mTaskInfo.apply {
+ configuration.windowConfiguration.displayRotation = Surface.ROTATION_0
+ }
+ // Test portrait stable bounds
+ performDrag(
+ STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(),
+ STARTING_BOUNDS.right.toFloat() + 2000, STARTING_BOUNDS.bottom.toFloat() + 2000,
+ CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM)
+ rectAfterDrag.right -= 2000
+ rectAfterDrag.bottom += 2000
+
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.configuration.windowConfiguration.bounds == rectAfterDrag
+ }
+ })
+ // Display has rotated; we expect a new stable bounds.
+ verify(mockDisplayLayout, Mockito.times(2)).getStableBounds(any())
+ }
+
+ private fun performDrag(
+ startX: Float,
+ startY: Float,
+ endX: Float,
+ endY: Float,
+ ctrlType: Int
+ ) {
+ taskPositioner.onDragPositioningStart(
+ ctrlType,
+ startX,
+ startY
+ )
+ taskPositioner.onDragPositioningMove(
+ endX,
+ endY
+ )
+
+ taskPositioner.onDragPositioningEnd(
+ endX,
+ endY
+ )
+ }
+
companion object {
private const val TASK_ID = 5
private const val MIN_WIDTH = 10
@@ -664,11 +749,17 @@
DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT,
DISPLAY_BOUNDS.right,
DISPLAY_BOUNDS.bottom)
- private val STABLE_BOUNDS = Rect(
+ private val STABLE_BOUNDS_LANDSCAPE = Rect(
DISPLAY_BOUNDS.left,
DISPLAY_BOUNDS.top + CAPTION_HEIGHT,
DISPLAY_BOUNDS.right,
DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT
)
+ private val STABLE_BOUNDS_PORTRAIT = Rect(
+ DISPLAY_BOUNDS.top,
+ DISPLAY_BOUNDS.left + CAPTION_HEIGHT,
+ DISPLAY_BOUNDS.bottom,
+ DISPLAY_BOUNDS.right - NAVBAR_HEIGHT
+ )
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
index 8913453..a70ebf1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
@@ -21,6 +21,9 @@
import android.os.IBinder
import android.testing.AndroidTestingRunner
import android.view.Display
+import android.view.Surface.ROTATION_0
+import android.view.Surface.ROTATION_270
+import android.view.Surface.ROTATION_90
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CHANGE
import android.window.WindowContainerToken
@@ -30,6 +33,7 @@
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED
@@ -93,10 +97,17 @@
whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
whenever(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)
whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
- (i.arguments.first() as Rect).set(STABLE_BOUNDS)
+ if (mockDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration
+ .displayRotation == ROTATION_90 ||
+ mockDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration
+ .displayRotation == ROTATION_270
+ ) {
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS_LANDSCAPE)
+ } else {
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS_PORTRAIT)
+ }
}
`when`(mockTransactionFactory.get()).thenReturn(mockTransaction)
-
mockDesktopWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
taskId = TASK_ID
token = taskToken
@@ -105,6 +116,7 @@
defaultMinSize = DEFAULT_MIN
displayId = DISPLAY_ID
configuration.windowConfiguration.setBounds(STARTING_BOUNDS)
+ configuration.windowConfiguration.displayRotation = ROTATION_90
}
mockDesktopWindowDecoration.mDisplay = mockDisplay
whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
@@ -343,7 +355,7 @@
)
val newX = STARTING_BOUNDS.left.toFloat()
- val newY = STABLE_BOUNDS.top.toFloat() - 5
+ val newY = STABLE_BOUNDS_LANDSCAPE.top.toFloat() - 5
taskPositioner.onDragPositioningMove(
newX,
newY
@@ -361,11 +373,79 @@
token == taskBinder &&
(change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
change.configuration.windowConfiguration.bounds.top ==
- STABLE_BOUNDS.top
+ STABLE_BOUNDS_LANDSCAPE.top
}
})
}
+ @Test
+ fun testDragResize_drag_updatesStableBoundsOnRotate() {
+ // Test landscape stable bounds
+ performDrag(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(),
+ STARTING_BOUNDS.right.toFloat() + 2000, STARTING_BOUNDS.bottom.toFloat() + 2000,
+ CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM)
+ val rectAfterDrag = Rect(STARTING_BOUNDS)
+ rectAfterDrag.right += 2000
+ // First drag; we should fetch stable bounds.
+ verify(mockDisplayLayout, times(1)).getStableBounds(any())
+ verify(mockTransitions).startTransition(eq(TRANSIT_CHANGE), argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.configuration.windowConfiguration.bounds == rectAfterDrag}},
+ eq(taskPositioner))
+ // Drag back to starting bounds.
+ performDrag(STARTING_BOUNDS.right.toFloat() + 2000, STARTING_BOUNDS.bottom.toFloat(),
+ STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(),
+ CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM)
+
+ // Display did not rotate; we should use previous stable bounds
+ verify(mockDisplayLayout, times(1)).getStableBounds(any())
+
+ // Rotate the screen to portrait
+ mockDesktopWindowDecoration.mTaskInfo.apply {
+ configuration.windowConfiguration.displayRotation = ROTATION_0
+ }
+ // Test portrait stable bounds
+ performDrag(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(),
+ STARTING_BOUNDS.right.toFloat() + 2000, STARTING_BOUNDS.bottom.toFloat() + 2000,
+ CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM)
+ rectAfterDrag.right -= 2000
+ rectAfterDrag.bottom += 2000
+
+ verify(mockTransitions).startTransition(eq(TRANSIT_CHANGE), argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.configuration.windowConfiguration.bounds == rectAfterDrag}},
+ eq(taskPositioner))
+ // Display has rotated; we expect a new stable bounds.
+ verify(mockDisplayLayout, times(2)).getStableBounds(any())
+ }
+
+ private fun performDrag(
+ startX: Float,
+ startY: Float,
+ endX: Float,
+ endY: Float,
+ ctrlType: Int
+ ) {
+ taskPositioner.onDragPositioningStart(
+ ctrlType,
+ startX,
+ startY
+ )
+ taskPositioner.onDragPositioningMove(
+ endX,
+ endY
+ )
+
+ taskPositioner.onDragPositioningEnd(
+ endX,
+ endY
+ )
+ }
+
companion object {
private const val TASK_ID = 5
private const val MIN_WIDTH = 10
@@ -378,11 +458,17 @@
private const val DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT = 10
private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
private val STARTING_BOUNDS = Rect(100, 100, 200, 200)
- private val STABLE_BOUNDS = Rect(
+ private val STABLE_BOUNDS_LANDSCAPE = Rect(
DISPLAY_BOUNDS.left,
DISPLAY_BOUNDS.top + CAPTION_HEIGHT,
DISPLAY_BOUNDS.right,
DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT
)
+ private val STABLE_BOUNDS_PORTRAIT = Rect(
+ DISPLAY_BOUNDS.top,
+ DISPLAY_BOUNDS.left + CAPTION_HEIGHT,
+ DISPLAY_BOUNDS.bottom,
+ DISPLAY_BOUNDS.right - NAVBAR_HEIGHT
+ )
}
}
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 7eb0c76..4a5b4f2 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -74,8 +74,7 @@
// Methods for MediaRouter2Manager
List<RoutingSessionInfo> getRemoteSessions(IMediaRouter2Manager manager);
- RoutingSessionInfo getSystemSessionInfoForPackage(
- IMediaRouter2Manager manager, String packageName);
+ RoutingSessionInfo getSystemSessionInfoForPackage(String packageName);
void registerManager(IMediaRouter2Manager manager, String packageName);
void unregisterManager(IMediaRouter2Manager manager);
void setRouteVolumeWithManager(IMediaRouter2Manager manager, int requestId,
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 2169090..159427b 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -2059,9 +2059,7 @@
public RoutingSessionInfo getSystemSessionInfo() {
RoutingSessionInfo result;
try {
- result =
- mMediaRouterService.getSystemSessionInfoForPackage(
- mClient, mClientPackageName);
+ result = mMediaRouterService.getSystemSessionInfoForPackage(mClientPackageName);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 3abfc629..830708c 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -377,7 +377,7 @@
@Nullable
public RoutingSessionInfo getSystemRoutingSession(@Nullable String packageName) {
try {
- return mMediaRouterService.getSystemSessionInfoForPackage(mClient, packageName);
+ return mMediaRouterService.getSystemSessionInfoForPackage(packageName);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 01596d2..d62b490 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -40,6 +40,7 @@
import com.android.settingslib.spa.gallery.page.ProgressBarPageProvider
import com.android.settingslib.spa.gallery.page.SettingsPagerPageProvider
import com.android.settingslib.spa.gallery.page.SliderPageProvider
+import com.android.settingslib.spa.gallery.preference.ListPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.MainSwitchPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
import com.android.settingslib.spa.gallery.preference.PreferencePageProvider
@@ -74,6 +75,7 @@
PreferencePageProvider,
SwitchPreferencePageProvider,
MainSwitchPreferencePageProvider,
+ ListPreferencePageProvider,
TwoTargetSwitchPreferencePageProvider,
ArgumentPageProvider,
SliderPageProvider,
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/ListPreferencePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/ListPreferencePageProvider.kt
new file mode 100644
index 0000000..43b6d0b
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/ListPreferencePageProvider.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.gallery.preference
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.ListPreference
+import com.android.settingslib.spa.widget.preference.ListPreferenceModel
+import com.android.settingslib.spa.widget.preference.ListPreferenceOption
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.flow
+
+private const val TITLE = "Sample ListPreference"
+
+object ListPreferencePageProvider : SettingsPageProvider {
+ override val name = "ListPreference"
+ private val owner = createSettingsPage()
+
+ override fun buildEntry(arguments: Bundle?) = listOf(
+ SettingsEntryBuilder.create("ListPreference", owner)
+ .setUiLayoutFn {
+ SampleListPreference()
+ }.build(),
+ SettingsEntryBuilder.create("ListPreference not changeable", owner)
+ .setUiLayoutFn {
+ SampleNotChangeableListPreference()
+ }.build(),
+ )
+
+ fun buildInjectEntry(): SettingsEntryBuilder {
+ return SettingsEntryBuilder.createInject(owner)
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+ }
+
+ override fun getTitle(arguments: Bundle?) = TITLE
+}
+
+@Composable
+private fun SampleListPreference() {
+ val selectedId = rememberSaveable { mutableIntStateOf(1) }
+ ListPreference(remember {
+ object : ListPreferenceModel {
+ override val title = "Preferred network type"
+ override val options = listOf(
+ ListPreferenceOption(id = 1, text = "5G (recommended)"),
+ ListPreferenceOption(id = 2, text = "LTE"),
+ ListPreferenceOption(id = 3, text = "3G"),
+ )
+ override val selectedId = selectedId
+ override val onIdSelected: (id: Int) -> Unit = { selectedId.intValue = it }
+ }
+ })
+}
+
+@Composable
+private fun SampleNotChangeableListPreference() {
+ val selectedId = rememberSaveable { mutableIntStateOf(1) }
+ val enableFlow = flow {
+ var enabled = true
+ while (true) {
+ delay(3.seconds)
+ enabled = !enabled
+ emit(enabled)
+ }
+ }
+ val enabled = enableFlow.collectAsStateWithLifecycle(initialValue = true)
+ ListPreference(remember {
+ object : ListPreferenceModel {
+ override val title = "Preferred network type"
+ override val enabled = enabled
+ override val options = listOf(
+ ListPreferenceOption(id = 1, text = "5G (recommended)"),
+ ListPreferenceOption(id = 2, text = "LTE"),
+ ListPreferenceOption(id = 3, text = "3G"),
+ )
+ override val selectedId = selectedId
+ override val onIdSelected: (id: Int) -> Unit = { selectedId.intValue = it }
+ }
+ })
+}
+
+@Preview
+@Composable
+private fun ListPreferencePagePreview() {
+ SettingsTheme {
+ ListPreferencePageProvider.Page(null)
+ }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMainPageProvider.kt
similarity index 95%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMainPageProvider.kt
index eddede7..ce9678b 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMainPageProvider.kt
@@ -36,6 +36,7 @@
PreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SwitchPreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
MainSwitchPreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+ ListPreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
TwoTargetSwitchPreferencePageProvider.buildInjectEntry()
.setLink(fromPage = owner).build(),
)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
index 7962e60..4088ffd 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
@@ -40,8 +40,18 @@
/** The size when app icon is displayed in App info page. */
val appIconInfoSize = 48.dp
+ /** The vertical padding for buttons. */
+ val buttonPaddingVertical = 12.dp
+
/** The [PaddingValues] for buttons. */
- val buttonPadding = PaddingValues(horizontal = itemPaddingEnd, vertical = 12.dp)
+ val buttonPadding = PaddingValues(horizontal = itemPaddingEnd, vertical = buttonPaddingVertical)
+
+ /** The horizontal padding for dialog items. */
+ val dialogItemPaddingHorizontal = itemPaddingStart
+
+ /** The [PaddingValues] for dialog items. */
+ val dialogItemPadding =
+ PaddingValues(horizontal = dialogItemPaddingHorizontal, vertical = buttonPaddingVertical)
/** The sizes info of illustration widget. */
val illustrationMaxWidth = 412.dp
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsOpacity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsOpacity.kt
index c8faef6..a9cd0e9 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsOpacity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsOpacity.kt
@@ -16,10 +16,15 @@
package com.android.settingslib.spa.framework.theme
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
+
object SettingsOpacity {
const val Full = 1f
const val Disabled = 0.38f
const val Divider = 0.2f
const val SurfaceTone = 0.14f
const val Hint = 0.9f
+
+ fun Modifier.alphaForEnabled(enabled: Boolean) = alpha(if (enabled) Full else Disabled)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
index c66e20a..8c862d4 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
@@ -22,5 +22,5 @@
object SettingsShape {
val CornerMedium = RoundedCornerShape(12.dp)
- val CornerLarge = RoundedCornerShape(24.dp)
+ val CornerExtraLarge = RoundedCornerShape(28.dp)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/EntryHighlight.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/EntryHighlight.kt
index 90c44b5..330755c 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/EntryHighlight.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/EntryHighlight.kt
@@ -30,11 +30,11 @@
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import com.android.settingslib.spa.framework.common.LocalEntryDataProvider
-import com.android.settingslib.spa.framework.theme.SettingsTheme
@Composable
-internal fun EntryHighlight(UiLayoutFn: @Composable () -> Unit) {
+internal fun EntryHighlight(content: @Composable () -> Unit) {
val entryData = LocalEntryDataProvider.current
val entryIsHighlighted = rememberSaveable { entryData.isHighlighted }
var localHighlighted by rememberSaveable { mutableStateOf(false) }
@@ -45,15 +45,16 @@
val backgroundColor by animateColorAsState(
targetValue = when {
localHighlighted -> MaterialTheme.colorScheme.surfaceVariant
- else -> SettingsTheme.colorScheme.background
+ else -> Color.Transparent
},
animationSpec = repeatable(
iterations = 3,
animation = tween(durationMillis = 500),
repeatMode = RepeatMode.Restart
- )
+ ),
+ label = "BackgroundColorAnimation",
)
Box(modifier = Modifier.background(color = backgroundColor)) {
- UiLayoutFn()
+ content()
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
index 1ad075c..979cf3b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
@@ -65,7 +65,7 @@
Row(
Modifier
.padding(SettingsDimension.buttonPadding)
- .clip(SettingsShape.CornerLarge)
+ .clip(SettingsShape.CornerExtraLarge)
.height(IntrinsicSize.Min)
) {
for ((index, actionButton) in actionButtons.withIndex()) {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt
new file mode 100644
index 0000000..8b172da
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.dialog
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Card
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.window.Dialog
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsShape
+import com.android.settingslib.spa.widget.ui.SettingsTitle
+
+@Composable
+fun SettingsDialog(
+ title: String,
+ onDismissRequest: () -> Unit,
+ content: @Composable () -> Unit,
+) {
+ Dialog(onDismissRequest = onDismissRequest) {
+ Card(shape = SettingsShape.CornerExtraLarge) {
+ Column(modifier = Modifier.padding(vertical = SettingsDimension.itemPaddingAround)) {
+ Box(modifier = Modifier.padding(SettingsDimension.dialogItemPadding)) {
+ SettingsTitle(title = title, useMediumWeight = true)
+ }
+ content()
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
index 6330ddf..4d42fba 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
@@ -29,13 +29,12 @@
import androidx.compose.runtime.State
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.alpha
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.framework.theme.SettingsDimension
-import com.android.settingslib.spa.framework.theme.SettingsOpacity
+import com.android.settingslib.spa.framework.theme.SettingsOpacity.alphaForEnabled
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.ui.SettingsTitle
@@ -57,8 +56,7 @@
.padding(end = paddingEnd),
verticalAlignment = Alignment.CenterVertically,
) {
- val alphaModifier =
- Modifier.alpha(if (enabled.value) SettingsOpacity.Full else SettingsOpacity.Disabled)
+ val alphaModifier = Modifier.alphaForEnabled(enabled.value)
BaseIcon(icon, alphaModifier, paddingStart)
Titles(
title = title,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt
new file mode 100644
index 0000000..19779f6
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.preference
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.selection.selectable
+import androidx.compose.foundation.selection.selectableGroup
+import androidx.compose.material3.RadioButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.IntState
+import androidx.compose.runtime.State
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.Role
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.widget.dialog.SettingsDialog
+import com.android.settingslib.spa.widget.ui.SettingsDialogItem
+
+data class ListPreferenceOption(
+ val id: Int,
+ val text: String,
+)
+
+/**
+ * The widget model for [ListPreference] widget.
+ */
+interface ListPreferenceModel {
+ /**
+ * The title of this [ListPreference].
+ */
+ val title: String
+
+ /**
+ * The icon of this [ListPreference].
+ *
+ * Default is `null` which means no icon.
+ */
+ val icon: (@Composable () -> Unit)?
+ get() = null
+
+ /**
+ * Indicates whether this [ListPreference] is enabled.
+ *
+ * Disabled [ListPreference] will be displayed in disabled style.
+ */
+ val enabled: State<Boolean>
+ get() = stateOf(true)
+
+ val options: List<ListPreferenceOption>
+
+ val selectedId: IntState
+
+ val onIdSelected: (id: Int) -> Unit
+}
+
+@Composable
+fun ListPreference(model: ListPreferenceModel) {
+ var dialogOpened by rememberSaveable { mutableStateOf(false) }
+ if (dialogOpened) {
+ SettingsDialog(
+ title = model.title,
+ onDismissRequest = { dialogOpened = false },
+ ) {
+ Column(modifier = Modifier.selectableGroup()) {
+ for (option in model.options) {
+ Radio(option, model.selectedId, model.enabled) {
+ dialogOpened = false
+ model.onIdSelected(it)
+ }
+ }
+ }
+ }
+ }
+ Preference(model = remember(model) {
+ object : PreferenceModel {
+ override val title = model.title
+ override val summary = derivedStateOf {
+ model.options.find { it.id == model.selectedId.intValue }?.text ?: ""
+ }
+ override val icon = model.icon
+ override val enabled = model.enabled
+ override val onClick = { dialogOpened = true }.takeIf { model.options.isNotEmpty() }
+ }
+ })
+}
+
+@Composable
+private fun Radio(
+ option: ListPreferenceOption,
+ selectedId: IntState,
+ enabledState: State<Boolean>,
+ onIdSelected: (id: Int) -> Unit,
+) {
+ val selected = option.id == selectedId.intValue
+ val enabled = enabledState.value
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .selectable(
+ selected = selected,
+ enabled = enabled,
+ onClick = { onIdSelected(option.id) },
+ role = Role.RadioButton,
+ )
+ .padding(SettingsDimension.dialogItemPadding),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ RadioButton(selected = selected, onClick = null, enabled = enabled)
+ Spacer(modifier = Modifier.width(SettingsDimension.itemPaddingEnd))
+ SettingsDialogItem(text = option.text, enabled = enabled)
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
index 3e04b16..0c16c8b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
@@ -39,7 +39,7 @@
true -> MaterialTheme.colorScheme.primaryContainer
else -> MaterialTheme.colorScheme.secondaryContainer
},
- shape = SettingsShape.CornerLarge,
+ shape = SettingsShape.CornerExtraLarge,
) {
InternalSwitchPreference(
title = model.title,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
index 6ef4590..5f320f7 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
@@ -18,19 +18,14 @@
import androidx.appcompat.R
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.outlined.ArrowBack
import androidx.compose.material.icons.outlined.Clear
import androidx.compose.material.icons.outlined.FindInPage
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
-import androidx.compose.ui.draw.scale
-import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.LayoutDirection
import com.android.settingslib.spa.framework.compose.LocalNavController
-import androidx.compose.material.icons.automirrored.outlined.ArrowBack
/** Action that navigates back to last page. */
@Composable
@@ -55,7 +50,6 @@
Icon(
imageVector = Icons.AutoMirrored.Outlined.ArrowBack,
contentDescription = contentDescription,
- modifier = Modifier.autoMirrored(),
)
}
}
@@ -81,10 +75,3 @@
)
}
}
-
-private fun Modifier.autoMirrored() = composed {
- when (LocalLayoutDirection.current) {
- LayoutDirection.Rtl -> scale(scaleX = -1f, scaleY = 1f)
- else -> this
- }
-}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
index 57319e7..7f1acff 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
@@ -30,6 +30,7 @@
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsOpacity.alphaForEnabled
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.theme.toMediumWeight
@@ -48,6 +49,17 @@
}
@Composable
+fun SettingsDialogItem(text: String, enabled: Boolean = true) {
+ Text(
+ text = text,
+ modifier = Modifier.alphaForEnabled(enabled),
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.bodyLarge,
+ overflow = TextOverflow.Ellipsis,
+ )
+}
+
+@Composable
fun SettingsBody(
body: String,
maxLines: Int = Int.MAX_VALUE,
@@ -82,6 +94,9 @@
private fun BasePreferencePreview() {
SettingsTheme {
Column(Modifier.width(100.dp)) {
+ SettingsTitle(
+ title = "Title",
+ )
SettingsBody(
body = "Long long long long long long text",
)
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsDialogTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsDialogTest.kt
new file mode 100644
index 0000000..c7582b2
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsDialogTest.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.dialog
+
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.onDialogText
+import com.android.settingslib.spa.widget.ui.SettingsDialogItem
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsDialogTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun title_displayed() {
+ composeTestRule.setContent {
+ SettingsDialog(title = TITLE, onDismissRequest = {}) {}
+ }
+
+ composeTestRule.onDialogText(TITLE).assertIsDisplayed()
+ }
+
+ @Test
+ fun text_displayed() {
+ composeTestRule.setContent {
+ SettingsDialog(title = "", onDismissRequest = {}) {
+ SettingsDialogItem(text = TEXT)
+ }
+ }
+
+ composeTestRule.onDialogText(TEXT).assertIsDisplayed()
+ }
+
+ private companion object {
+ const val TITLE = "Title"
+ const val TEXT = "Text"
+ }
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt
new file mode 100644
index 0000000..997a023
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.preference
+
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.testutils.onDialogText
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ListPreferenceTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun title_displayed() {
+ composeTestRule.setContent {
+ ListPreference(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val options = emptyList<ListPreferenceOption>()
+ override val selectedId = mutableIntStateOf(0)
+ override val onIdSelected: (Int) -> Unit = {}
+ }
+ })
+ }
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed()
+ }
+
+ @Test
+ fun summary_showSelectedText() {
+ composeTestRule.setContent {
+ ListPreference(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val options = listOf(ListPreferenceOption(id = 1, text = "A"))
+ override val selectedId = mutableIntStateOf(1)
+ override val onIdSelected: (Int) -> Unit = {}
+ }
+ })
+ }
+
+ composeTestRule.onNodeWithText("A").assertIsDisplayed()
+ }
+
+ @Test
+ fun click_optionsIsEmpty_notShowDialog() {
+ composeTestRule.setContent {
+ ListPreference(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val options = emptyList<ListPreferenceOption>()
+ override val selectedId = mutableIntStateOf(0)
+ override val onIdSelected: (Int) -> Unit = {}
+ }
+ })
+ }
+
+ composeTestRule.onNodeWithText(TITLE).performClick()
+
+ composeTestRule.onDialogText(TITLE).assertDoesNotExist()
+ }
+
+ @Test
+ fun click_notEnabled_notShowDialog() {
+ composeTestRule.setContent {
+ ListPreference(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val enabled = stateOf(false)
+ override val options = listOf(ListPreferenceOption(id = 1, text = "A"))
+ override val selectedId = mutableIntStateOf(1)
+ override val onIdSelected: (Int) -> Unit = {}
+ }
+ })
+ }
+
+ composeTestRule.onNodeWithText(TITLE).performClick()
+
+ composeTestRule.onDialogText(TITLE).assertDoesNotExist()
+ }
+
+ @Test
+ fun click_optionsNotEmpty_showDialog() {
+ composeTestRule.setContent {
+ ListPreference(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val options = listOf(ListPreferenceOption(id = 1, text = "A"))
+ override val selectedId = mutableIntStateOf(1)
+ override val onIdSelected: (Int) -> Unit = {}
+ }
+ })
+ }
+
+ composeTestRule.onNodeWithText(TITLE).performClick()
+
+ composeTestRule.onDialogText(TITLE).assertIsDisplayed()
+ }
+
+ @Test
+ fun select() {
+ val selectedId = mutableIntStateOf(1)
+ composeTestRule.setContent {
+ ListPreference(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val options = listOf(
+ ListPreferenceOption(id = 1, text = "A"),
+ ListPreferenceOption(id = 2, text = "B"),
+ )
+ override val selectedId = selectedId
+ override val onIdSelected = { id: Int -> selectedId.intValue = id }
+ }
+ })
+ }
+
+ composeTestRule.onNodeWithText(TITLE).performClick()
+ composeTestRule.onDialogText("B").performClick()
+
+ composeTestRule.onNodeWithText("B").assertIsDisplayed()
+ }
+
+ @Test
+ fun select_dialogOpenThenDisable_itemAlsoDisabled() {
+ val selectedId = mutableIntStateOf(1)
+ val enabledState = mutableStateOf(true)
+ composeTestRule.setContent {
+ ListPreference(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val enabled = enabledState
+ override val options = listOf(
+ ListPreferenceOption(id = 1, text = "A"),
+ ListPreferenceOption(id = 2, text = "B"),
+ )
+ override val selectedId = selectedId
+ override val onIdSelected = { id: Int -> selectedId.intValue = id }
+ }
+ })
+ }
+
+ composeTestRule.onNodeWithText(TITLE).performClick()
+ enabledState.value = false
+
+ composeTestRule.onDialogText("B").assertIsDisplayed().assertIsNotEnabled()
+ }
+
+ private companion object {
+ const val TITLE = "Title"
+ }
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index fe39c4f..59c3cd3 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -84,6 +84,7 @@
Settings.System.RING_VIBRATION_INTENSITY,
Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY,
+ Settings.System.KEYBOARD_VIBRATION_ENABLED,
Settings.System.HAPTIC_FEEDBACK_ENABLED,
Settings.System.DISPLAY_COLOR_MODE_VENDOR_HINT, // must precede DISPLAY_COLOR_MODE
Settings.System.DISPLAY_COLOR_MODE,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index eba74ab..572303a 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -138,6 +138,7 @@
VALIDATORS.put(System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
VALIDATORS.put(System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
VALIDATORS.put(System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
+ VALIDATORS.put(System.KEYBOARD_VIBRATION_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.HAPTIC_FEEDBACK_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.RINGTONE, URI_VALIDATOR);
VALIDATORS.put(System.NOTIFICATION_SOUND, URI_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 46cd725..4e2fad0 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3243,7 +3243,7 @@
}
if (success && criticalSettings != null && criticalSettings.contains(name)) {
- settingsState.persistSyncLocked();
+ settingsState.persistSettingsLocked();
}
if (forceNotify || success) {
@@ -3294,7 +3294,7 @@
}
if (success && criticalSettings != null && criticalSettings.contains(name)) {
- settingsState.persistSyncLocked();
+ settingsState.persistSettingsLocked();
}
if (forceNotify || success) {
@@ -3319,7 +3319,7 @@
}
if (success && criticalSettings != null && criticalSettings.contains(name)) {
- settingsState.persistSyncLocked();
+ settingsState.persistSettingsLocked();
}
if (forceNotify || success) {
@@ -3385,7 +3385,7 @@
}
}
if (someSettingChanged) {
- settingsState.persistSyncLocked();
+ settingsState.persistSettingsLocked();
success = true;
}
}
@@ -3407,7 +3407,7 @@
}
}
if (someSettingChanged) {
- settingsState.persistSyncLocked();
+ settingsState.persistSettingsLocked();
success = true;
}
}
@@ -3435,7 +3435,7 @@
}
}
if (someSettingChanged) {
- settingsState.persistSyncLocked();
+ settingsState.persistSettingsLocked();
success = true;
}
}
@@ -3460,7 +3460,7 @@
logSettingChanged(userId, name, type, CHANGE_TYPE_DELETE);
}
if (someSettingChanged) {
- settingsState.persistSyncLocked();
+ settingsState.persistSettingsLocked();
success = true;
}
}
@@ -3559,7 +3559,7 @@
ensureSettingsStateLocked(systemKey);
SettingsState systemSettings = mSettingsStates.get(systemKey);
migrateLegacySettingsLocked(systemSettings, database, TABLE_SYSTEM);
- systemSettings.persistSyncLocked();
+ systemSettings.persistSettingsLocked();
// Move over the secure settings.
// Do this after System settings, since this is the first thing we check when deciding
@@ -3569,7 +3569,7 @@
SettingsState secureSettings = mSettingsStates.get(secureKey);
migrateLegacySettingsLocked(secureSettings, database, TABLE_SECURE);
ensureSecureSettingAndroidIdSetLocked(secureSettings);
- secureSettings.persistSyncLocked();
+ secureSettings.persistSettingsLocked();
// Move over the global settings if owner.
// Do this last, since this is the first thing we check when deciding
@@ -3585,7 +3585,7 @@
mSettingsCreationBuildId, null, true,
SettingsState.SYSTEM_PACKAGE_NAME);
}
- globalSettings.persistSyncLocked();
+ globalSettings.persistSettingsLocked();
}
// Drop the database as now all is moved and persisted.
@@ -4404,16 +4404,16 @@
if (userId == UserHandle.USER_SYSTEM) {
SettingsState globalSettings = getGlobalSettingsLocked();
ensureLegacyDefaultValueAndSystemSetUpdatedLocked(globalSettings, userId);
- globalSettings.persistSyncLocked();
+ globalSettings.persistSettingsLocked();
}
SettingsState secureSettings = getSecureSettingsLocked(mUserId);
ensureLegacyDefaultValueAndSystemSetUpdatedLocked(secureSettings, userId);
- secureSettings.persistSyncLocked();
+ secureSettings.persistSettingsLocked();
SettingsState systemSettings = getSystemSettingsLocked(mUserId);
ensureLegacyDefaultValueAndSystemSetUpdatedLocked(systemSettings, userId);
- systemSettings.persistSyncLocked();
+ systemSettings.persistSettingsLocked();
currentVersion = 146;
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index e9533e5..7cec99d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -72,6 +72,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
/**
* This class contains the state for one type of settings. It is responsible
@@ -589,9 +590,10 @@
}
// The settings provider must hold its lock when calling here.
- public void persistSyncLocked() {
+ public void persistSettingsLocked() {
mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS);
- doWriteState();
+ // schedule a write operation right away
+ mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS).sendToTarget();
}
// The settings provider must hold its lock when calling here.
@@ -1725,4 +1727,20 @@
return mPackageToMemoryUsage.getOrDefault(packageName, 0);
}
}
+
+ /**
+ * Allow tests to wait for the handler to finish handling all the remaining messages
+ */
+ @VisibleForTesting
+ public void waitForHandler() {
+ final CountDownLatch latch = new CountDownLatch(1);
+ synchronized (mLock) {
+ mHandler.post(latch::countDown);
+ }
+ try {
+ latch.await();
+ } catch (InterruptedException e) {
+ // ignored
+ }
+ }
}
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index df4d2a1..02a7bc1 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -76,6 +76,14 @@
mSettingsFile.delete();
}
+ @Override
+ protected void tearDown() throws Exception {
+ if (mSettingsFile != null) {
+ mSettingsFile.delete();
+ }
+ super.tearDown();
+ }
+
public void testIsBinary() {
assertFalse(SettingsState.isBinary(" abc 日本語"));
@@ -149,11 +157,10 @@
* Make sure settings can be written to a file and also can be read.
*/
public void testReadWrite() {
- final File file = new File(getContext().getCacheDir(), "setting.xml");
- file.delete();
final Object lock = new Object();
- final SettingsState ssWriter = new SettingsState(getContext(), lock, file, 1,
+ assertFalse(mSettingsFile.exists());
+ final SettingsState ssWriter = new SettingsState(getContext(), lock, mSettingsFile, 1,
SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
ssWriter.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING);
@@ -162,11 +169,13 @@
ssWriter.insertSettingLocked("k3", null, null, false, "p2");
ssWriter.insertSettingLocked("k4", CRAZY_STRING, null, false, "p3");
synchronized (lock) {
- ssWriter.persistSyncLocked();
+ ssWriter.persistSettingsLocked();
}
-
- final SettingsState ssReader = new SettingsState(getContext(), lock, file, 1,
+ ssWriter.waitForHandler();
+ assertTrue(mSettingsFile.exists());
+ final SettingsState ssReader = new SettingsState(getContext(), lock, mSettingsFile, 1,
SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
+
synchronized (lock) {
assertEquals("\u0000", ssReader.getSettingLocked("k1").getValue());
assertEquals("abc", ssReader.getSettingLocked("k2").getValue());
@@ -179,10 +188,8 @@
* In version 120, value "null" meant {code NULL}.
*/
public void testUpgrade() throws Exception {
- final File file = new File(getContext().getCacheDir(), "setting.xml");
- file.delete();
final Object lock = new Object();
- final PrintStream os = new PrintStream(new FileOutputStream(file));
+ final PrintStream os = new PrintStream(new FileOutputStream(mSettingsFile));
os.print(
"<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>" +
"<settings version=\"120\">" +
@@ -192,7 +199,7 @@
"</settings>");
os.close();
- final SettingsState ss = new SettingsState(getContext(), lock, file, 1,
+ final SettingsState ss = new SettingsState(getContext(), lock, mSettingsFile, 1,
SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
synchronized (lock) {
SettingsState.Setting s;
@@ -213,7 +220,8 @@
public void testInitializeSetting_preserveFlagNotSet() {
SettingsState settingsWriter = getSettingStateObject();
settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
- settingsWriter.persistSyncLocked();
+ settingsWriter.persistSettingsLocked();
+ settingsWriter.waitForHandler();
SettingsState settingsReader = getSettingStateObject();
assertFalse(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
@@ -223,7 +231,8 @@
SettingsState settingsWriter = getSettingStateObject();
settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, TEST_PACKAGE);
- settingsWriter.persistSyncLocked();
+ settingsWriter.persistSettingsLocked();
+ settingsWriter.waitForHandler();
SettingsState settingsReader = getSettingStateObject();
assertTrue(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
@@ -234,7 +243,8 @@
settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, false, TEST_PACKAGE,
/* overrideableByRestore */ true);
- settingsWriter.persistSyncLocked();
+ settingsWriter.persistSettingsLocked();
+ settingsWriter.waitForHandler();
SettingsState settingsReader = getSettingStateObject();
assertFalse(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
@@ -250,7 +260,8 @@
// already been set to true.
settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, false, TEST_PACKAGE,
/* overrideableByRestore */ true);
- settingsWriter.persistSyncLocked();
+ settingsWriter.persistSettingsLocked();
+ settingsWriter.waitForHandler();
SettingsState settingsReader = getSettingStateObject();
assertTrue(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
@@ -481,8 +492,11 @@
settingsState.insertSettingLocked(
FLAG_NAME_1_STAGED, VALUE1, null, false, TEST_PACKAGE);
settingsState.insertSettingLocked(FLAG_NAME_2, VALUE2, null, false, TEST_PACKAGE);
- settingsState.persistSyncLocked();
+ settingsState.persistSettingsLocked();
+ }
+ settingsState.waitForHandler();
+ synchronized (lock) {
assertEquals(VALUE1, settingsState.getSettingLocked(FLAG_NAME_1_STAGED).getValue());
assertEquals(VALUE2, settingsState.getSettingLocked(FLAG_NAME_2).getValue());
}
@@ -522,7 +536,10 @@
synchronized (lock) {
settingsState.insertSettingLocked(INVALID_STAGED_FLAG_1,
VALUE2, null, false, TEST_PACKAGE);
- settingsState.persistSyncLocked();
+ settingsState.persistSettingsLocked();
+ }
+ settingsState.waitForHandler();
+ synchronized (lock) {
assertEquals(VALUE2, settingsState.getSettingLocked(INVALID_STAGED_FLAG_1).getValue());
}
diff --git a/packages/SoundPicker/Android.bp b/packages/SoundPicker/Android.bp
index 235e672..2c89d6d 100644
--- a/packages/SoundPicker/Android.bp
+++ b/packages/SoundPicker/Android.bp
@@ -7,40 +7,22 @@
default_applicable_licenses: ["frameworks_base_license"],
}
-android_library {
- name: "SoundPickerLib",
- srcs: [
- "src/**/*.java",
- ],
- resource_dirs: [
- "res",
- ],
- static_libs: [
- "androidx.appcompat_appcompat",
- "hilt_android",
- "guava",
- "androidx.recyclerview_recyclerview",
- "androidx-constraintlayout_constraintlayout",
- "androidx.viewpager2_viewpager2",
- "com.google.android.material_material",
- ],
-}
-
android_app {
name: "SoundPicker",
defaults: ["platform_app_defaults"],
manifest: "AndroidManifest.xml",
- static_libs: ["SoundPickerLib"],
+
+ static_libs: [
+ "androidx.appcompat_appcompat",
+ ],
+ resource_dirs: [
+ "res",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+
platform_apis: true,
certificate: "media",
privileged: true,
-
- optimize: {
- enabled: true,
- optimize: true,
- shrink: true,
- shrink_resources: true,
- obfuscate: false,
- proguard_compatibility: false,
- },
}
diff --git a/packages/SoundPicker/AndroidManifest.xml b/packages/SoundPicker/AndroidManifest.xml
index 934b003..44295a5 100644
--- a/packages/SoundPicker/AndroidManifest.xml
+++ b/packages/SoundPicker/AndroidManifest.xml
@@ -1,6 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.soundpicker"
- android:sharedUserId="android.media">
+ package="com.android.soundpicker"
+ android:sharedUserId="android.media">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
@@ -9,16 +9,12 @@
<uses-permission android:name="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
- <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
-
<application
- android:name=".RingtonePickerApplication"
- android:allowBackup="false"
- android:label="@string/app_label"
- android:theme="@style/Theme.AppCompat"
- android:supportsRtl="true">
+ android:allowBackup="false"
+ android:label="@string/app_label"
+ android:supportsRtl="true">
<receiver android:name="RingtoneReceiver"
- android:exported="true">
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY"/>
</intent-filter>
@@ -27,17 +23,14 @@
<service android:name="RingtoneOverlayService" />
<activity android:name="RingtonePickerActivity"
- android:theme="@style/Theme.AppCompat.Dialog"
- android:enabled="@*android:bool/config_defaultRingtonePickerEnabled"
- android:excludeFromRecents="true"
- android:exported="true">
+ android:theme="@style/PickerDialogTheme"
+ android:enabled="@*android:bool/config_defaultRingtonePickerEnabled"
+ android:excludeFromRecents="true"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.RINGTONE_PICKER" />
<category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.RINGTONE_PICKER_SOUND" />
- <category android:name="android.intent.category.RINGTONE_PICKER_VIBRATION" />
- <category android:name="android.intent.category.RINGTONE_PICKER_RINGTONE" />
</intent-filter>
</activity>
</application>
-</manifest>
+</manifest>
\ No newline at end of file
diff --git a/packages/SoundPicker/res/layout/add_new_sound_item.xml b/packages/SoundPicker/res/layout/add_new_sound_item.xml
index 024b97e..57b70d7 100644
--- a/packages/SoundPicker/res/layout/add_new_sound_item.xml
+++ b/packages/SoundPicker/res/layout/add_new_sound_item.xml
@@ -19,9 +19,7 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
- android:background="?android:attr/selectableItemBackground"
- android:focusable="true"
- android:clickable="true">
+ android:background="?android:attr/selectableItemBackground">
<ImageView
android:layout_width="24dp"
@@ -31,19 +29,19 @@
android:scaleType="centerCrop"
android:layout_marginRight="24dp"
android:layout_marginLeft="24dp"
- android:src="@drawable/ic_add"/>
+ android:src="@drawable/ic_add" />
- <TextView
- android:id="@+id/add_new_sound_text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
- android:text="@null"
- android:textColor="?android:attr/colorAccent"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:maxLines="3"
- android:gravity="center_vertical"
- android:paddingEnd="?android:attr/dialogPreferredPadding"
- android:drawablePadding="20dp"
- android:ellipsize="marquee"/>
+ <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/add_new_sound_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:text="@null"
+ android:textColor="?android:attr/colorAccent"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:maxLines="3"
+ android:gravity="center_vertical"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:drawablePadding="20dp"
+ android:ellipsize="marquee" />
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SoundPicker/res/layout/radio_with_work_badge.xml b/packages/SoundPicker/res/layout/radio_with_work_badge.xml
index 36ac93e..2e44b6f 100644
--- a/packages/SoundPicker/res/layout/radio_with_work_badge.xml
+++ b/packages/SoundPicker/res/layout/radio_with_work_badge.xml
@@ -14,14 +14,12 @@
limitations under the License.
-->
-<com.android.soundpicker.CheckedListItem
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:background="?android:attr/selectableItemBackground"
- android:focusable="true"
- android:clickable="true">
+<com.android.soundpicker.CheckedListItem xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackground"
+ >
<CheckedTextView
android:id="@+id/checked_text_view"
@@ -37,7 +35,7 @@
android:drawablePadding="20dp"
android:ellipsize="marquee"
android:layout_toLeftOf="@+id/work_icon"
- android:maxLines="3"/>
+ android:maxLines="3" />
<ImageView
android:id="@id/work_icon"
@@ -46,5 +44,5 @@
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:scaleType="centerCrop"
- android:layout_marginRight="20dp"/>
-</com.android.soundpicker.CheckedListItem>
+ android:layout_marginRight="20dp" />
+</com.android.soundpicker.CheckedListItem>
\ No newline at end of file
diff --git a/packages/SoundPicker/res/values/strings.xml b/packages/SoundPicker/res/values/strings.xml
index ab7b95a..04a2c2b 100644
--- a/packages/SoundPicker/res/values/strings.xml
+++ b/packages/SoundPicker/res/values/strings.xml
@@ -40,8 +40,4 @@
<!-- Text for the name of the app. [CHAR LIMIT=12] -->
<string name="app_label">Sounds</string>
-
- <string name="empty_list">The list is empty</string>
- <string name="sound_page_title">Sound</string>
- <string name="vibration_page_title">Vibration</string>
</resources>
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
index 90a14f9..ea46c0c 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
@@ -16,19 +16,43 @@
package com.android.soundpicker;
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.database.Cursor;
+import android.database.CursorWrapper;
+import android.media.AudioAttributes;
+import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.MediaStore;
+import android.provider.Settings;
import android.util.Log;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.CursorAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentTransaction;
-import androidx.lifecycle.ViewModelProvider;
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
-import dagger.hilt.android.AndroidEntryPoint;
+import java.io.IOException;
+import java.util.regex.Pattern;
/**
* The {@link RingtonePickerActivity} allows the user to choose one from all of the
@@ -36,183 +60,727 @@
*
* @see RingtoneManager#ACTION_RINGTONE_PICKER
*/
-@AndroidEntryPoint(AppCompatActivity.class)
-public final class RingtonePickerActivity extends Hilt_RingtonePickerActivity {
+public final class RingtonePickerActivity extends AlertActivity implements
+ AdapterView.OnItemSelectedListener, Runnable, DialogInterface.OnClickListener,
+ AlertController.AlertParams.OnPrepareListViewListener {
+
+ private static final int POS_UNKNOWN = -1;
private static final String TAG = "RingtonePickerActivity";
- // TODO: Use the extra keys from RingtoneManager once they're added.
- private static final String EXTRA_RINGTONE_PICKER_CATEGORY = "EXTRA_RINGTONE_PICKER_CATEGORY";
- private static final String EXTRA_VIBRATION_SHOW_DEFAULT = "EXTRA_VIBRATION_SHOW_DEFAULT";
- private static final String EXTRA_VIBRATION_DEFAULT_URI = "EXTRA_VIBRATION_DEFAULT_URI";
- private static final String EXTRA_VIBRATION_SHOW_SILENT = "EXTRA_VIBRATION_SHOW_SILENT";
- private static final String EXTRA_VIBRATION_EXISTING_URI = "EXTRA_VIBRATION_EXISTING_URI";
- private static final boolean RINGTONE_PICKER_CATEGORY_FEATURE_ENABLED = false;
- private RingtonePickerViewModel mRingtonePickerViewModel;
+ private static final int DELAY_MS_SELECTION_PLAYED = 300;
+
+ private static final String COLUMN_LABEL = MediaStore.Audio.Media.TITLE;
+
+ private static final String SAVE_CLICKED_POS = "clicked_pos";
+
+ private static final String SOUND_NAME_RES_PREFIX = "sound_name_";
+
+ private static final int ADD_FILE_REQUEST_CODE = 300;
+
+ private RingtoneManager mRingtoneManager;
+ private int mType;
+
+ private Cursor mCursor;
+ private Handler mHandler;
+ private BadgedRingtoneAdapter mAdapter;
+
+ /** The position in the list of the 'Silent' item. */
+ private int mSilentPos = POS_UNKNOWN;
+
+ /** The position in the list of the 'Default' item. */
+ private int mDefaultRingtonePos = POS_UNKNOWN;
+
+ /** The position in the list of the ringtone to sample. */
+ private int mSampleRingtonePos = POS_UNKNOWN;
+
+ /** Whether this list has the 'Silent' item. */
+ private boolean mHasSilentItem;
+
+ /** The Uri to place a checkmark next to. */
+ private Uri mExistingUri;
+
+ /** The number of static items in the list. */
+ private int mStaticItemCount;
+
+ /** Whether this list has the 'Default' item. */
+ private boolean mHasDefaultItem;
+
+ /** The Uri to play when the 'Default' item is clicked. */
+ private Uri mUriForDefaultItem;
+
+ /** Id of the user to which the ringtone picker should list the ringtones */
+ private int mPickerUserId;
+
+ /** Context of the user specified by mPickerUserId */
+ private Context mTargetContext;
+
+ /**
+ * A Ringtone for the default ringtone. In most cases, the RingtoneManager
+ * will stop the previous ringtone. However, the RingtoneManager doesn't
+ * manage the default ringtone for us, so we should stop this one manually.
+ */
+ private Ringtone mDefaultRingtone;
+
+ /**
+ * The ringtone that's currently playing, unless the currently playing one is the default
+ * ringtone.
+ */
+ private Ringtone mCurrentRingtone;
+
+ /**
+ * Stable ID for the ringtone that is currently checked (may be -1 if no ringtone is checked).
+ */
+ private long mCheckedItemId = -1;
+
private int mAttributesFlags;
+ private boolean mShowOkCancelButtons;
+
+ /**
+ * Keep the currently playing ringtone around when changing orientation, so that it
+ * can be stopped later, after the activity is recreated.
+ */
+ private static Ringtone sPlayingRingtone;
+
+ private DialogInterface.OnClickListener mRingtoneClickListener =
+ new DialogInterface.OnClickListener() {
+
+ /*
+ * On item clicked
+ */
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == mCursor.getCount() + mStaticItemCount) {
+ // The "Add new ringtone" item was clicked. Start a file picker intent to select
+ // only audio files (MIME type "audio/*")
+ final Intent chooseFile = getMediaFilePickerIntent();
+ startActivityForResult(chooseFile, ADD_FILE_REQUEST_CODE);
+ return;
+ }
+
+ // Save the position of most recently clicked item
+ setCheckedItem(which);
+
+ // In the buttonless (watch-only) version, preemptively set our result since we won't
+ // have another chance to do so before the activity closes.
+ if (!mShowOkCancelButtons) {
+ setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
+ }
+
+ // Play clip
+ playRingtone(which, 0);
+ }
+
+ };
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_ringtone_picker);
- mRingtonePickerViewModel = new ViewModelProvider(this).get(RingtonePickerViewModel.class);
+ mHandler = new Handler();
Intent intent = getIntent();
- /**
- * Id of the user to which the ringtone picker should list the ringtones
- */
- int pickerUserId = UserHandle.myUserId();
+ mPickerUserId = UserHandle.myUserId();
+ mTargetContext = this;
// Get the types of ringtones to show
- int ringtoneType = intent.getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE,
- RingtonePickerViewModel.RINGTONE_TYPE_UNKNOWN);
+ mType = intent.getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, -1);
+ initRingtoneManager();
+ /*
+ * Get whether to show the 'Default' item, and the URI to play when the
+ * default is clicked
+ */
+ mHasDefaultItem = intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
+ mUriForDefaultItem = intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI);
+ if (mUriForDefaultItem == null) {
+ if (mType == RingtoneManager.TYPE_NOTIFICATION) {
+ mUriForDefaultItem = Settings.System.DEFAULT_NOTIFICATION_URI;
+ } else if (mType == RingtoneManager.TYPE_ALARM) {
+ mUriForDefaultItem = Settings.System.DEFAULT_ALARM_ALERT_URI;
+ } else if (mType == RingtoneManager.TYPE_RINGTONE) {
+ mUriForDefaultItem = Settings.System.DEFAULT_RINGTONE_URI;
+ } else {
+ // or leave it null for silence.
+ mUriForDefaultItem = Settings.System.DEFAULT_RINGTONE_URI;
+ }
+ }
+
+ // Get whether to show the 'Silent' item
+ mHasSilentItem = intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
// AudioAttributes flags
mAttributesFlags |= intent.getIntExtra(
RingtoneManager.EXTRA_RINGTONE_AUDIO_ATTRIBUTES_FLAGS,
0 /*defaultValue == no flags*/);
- boolean showOkCancelButtons = getResources().getBoolean(R.bool.config_showOkCancelButtons);
-
- String title = intent.getStringExtra(RingtoneManager.EXTRA_RINGTONE_TITLE);
- if (title == null) {
- title = getString(RingtonePickerViewModel.getTitleByType(ringtoneType));
- }
- String ringtonePickerCategory = intent.getStringExtra(EXTRA_RINGTONE_PICKER_CATEGORY);
- RingtonePickerViewModel.PickerType pickerType = mapCategoryToPickerType(
- ringtonePickerCategory);
-
- RingtoneListHandler.Config soundListConfig = getSoundListConfig(pickerType, intent,
- ringtoneType);
- RingtoneListHandler.Config vibrationListConfig = getVibrationListConfig(pickerType, intent);
-
- RingtonePickerViewModel.Config pickerConfig =
- new RingtonePickerViewModel.Config(title, pickerUserId, ringtoneType,
- showOkCancelButtons, mAttributesFlags, pickerType);
-
- mRingtonePickerViewModel.init(pickerConfig, soundListConfig, vibrationListConfig);
-
- if (savedInstanceState == null) {
- TabbedDialogFragment dialogFragment = new TabbedDialogFragment();
-
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
- Fragment prev = getSupportFragmentManager().findFragmentByTag(TabbedDialogFragment.TAG);
- if (prev != null) {
- ft.remove(prev);
- }
- ft.addToBackStack(null);
- dialogFragment.show(ft, TabbedDialogFragment.TAG);
- }
+ mShowOkCancelButtons = getResources().getBoolean(R.bool.config_showOkCancelButtons);
// The volume keys will control the stream that we are choosing a ringtone for
- setVolumeControlStream(mRingtonePickerViewModel.getRingtoneStreamType());
- }
+ setVolumeControlStream(mRingtoneManager.inferStreamType());
- private RingtoneListHandler.Config getSoundListConfig(
- RingtonePickerViewModel.PickerType pickerType, Intent intent, int ringtoneType) {
- if (pickerType != RingtonePickerViewModel.PickerType.SOUND_PICKER
- && pickerType != RingtonePickerViewModel.PickerType.RINGTONE_PICKER) {
- // This ringtone picker does not require a sound picker.
- return null;
- }
-
- // Get whether to show the 'Default' sound item, and the URI to play when it's clicked
- boolean hasDefaultSoundItem =
- intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
-
- // The Uri to play when the 'Default' sound item is clicked.
- Uri uriForDefaultSoundItem =
- intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI);
- if (uriForDefaultSoundItem == null) {
- uriForDefaultSoundItem = RingtonePickerViewModel.getDefaultItemUriByType(ringtoneType);
- }
-
- // Get whether this list has the 'Silent' sound item.
- boolean hasSilentSoundItem =
- intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
-
- // AudioAttributes flags
- mAttributesFlags |= intent.getIntExtra(
- RingtoneManager.EXTRA_RINGTONE_AUDIO_ATTRIBUTES_FLAGS,
- 0 /*defaultValue == no flags*/);
-
- // Get the sound URI whose list item should have a checkmark
- Uri existingSoundUri = intent
+ // Get the URI whose list item should have a checkmark
+ mExistingUri = intent
.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI);
- return new RingtoneListHandler.Config(hasDefaultSoundItem,
- uriForDefaultSoundItem, hasSilentSoundItem, existingSoundUri);
- }
-
- private RingtoneListHandler.Config getVibrationListConfig(
- RingtonePickerViewModel.PickerType pickerType, Intent intent) {
- if (pickerType != RingtonePickerViewModel.PickerType.VIBRATION_PICKER
- && pickerType != RingtonePickerViewModel.PickerType.RINGTONE_PICKER) {
- // This ringtone picker does not require a vibration picker.
- return null;
+ // Create the list of ringtones and hold on to it so we can update later.
+ mAdapter = new BadgedRingtoneAdapter(this, mCursor,
+ /* isManagedProfile = */ UserManager.get(this).isManagedProfile(mPickerUserId));
+ if (savedInstanceState != null) {
+ setCheckedItem(savedInstanceState.getInt(SAVE_CLICKED_POS, POS_UNKNOWN));
}
- // Get whether to show the 'Default' vibration item, and the URI to play when it's clicked
- boolean hasDefaultVibrationItem =
- intent.getBooleanExtra(EXTRA_VIBRATION_SHOW_DEFAULT, false);
+ final AlertController.AlertParams p = mAlertParams;
+ p.mAdapter = mAdapter;
+ p.mOnClickListener = mRingtoneClickListener;
+ p.mLabelColumn = COLUMN_LABEL;
+ p.mIsSingleChoice = true;
+ p.mOnItemSelectedListener = this;
+ if (mShowOkCancelButtons) {
+ p.mPositiveButtonText = getString(com.android.internal.R.string.ok);
+ p.mPositiveButtonListener = this;
+ p.mNegativeButtonText = getString(com.android.internal.R.string.cancel);
+ p.mPositiveButtonListener = this;
+ }
+ p.mOnPrepareListViewListener = this;
- // The Uri to play when the 'Default' vibration item is clicked.
- Uri uriForDefaultVibrationItem = intent.getParcelableExtra(EXTRA_VIBRATION_DEFAULT_URI);
+ p.mTitle = intent.getCharSequenceExtra(RingtoneManager.EXTRA_RINGTONE_TITLE);
+ if (p.mTitle == null) {
+ if (mType == RingtoneManager.TYPE_ALARM) {
+ p.mTitle = getString(com.android.internal.R.string.ringtone_picker_title_alarm);
+ } else if (mType == RingtoneManager.TYPE_NOTIFICATION) {
+ p.mTitle =
+ getString(com.android.internal.R.string.ringtone_picker_title_notification);
+ } else {
+ p.mTitle = getString(com.android.internal.R.string.ringtone_picker_title);
+ }
+ }
- // Get whether this list has the 'Silent' vibration item.
- boolean hasSilentVibrationItem =
- intent.getBooleanExtra(EXTRA_VIBRATION_SHOW_SILENT, true);
+ setupAlert();
- // Get the vibration URI whose list item should have a checkmark
- Uri existingVibrationUri = intent.getParcelableExtra(EXTRA_VIBRATION_EXISTING_URI);
-
- return new RingtoneListHandler.Config(
- hasDefaultVibrationItem, uriForDefaultVibrationItem, hasSilentVibrationItem,
- existingVibrationUri);
+ ListView listView = mAlert.getListView();
+ if (listView != null) {
+ // List view needs to gain focus in order for RSB to work.
+ if (!listView.requestFocus()) {
+ Log.e(TAG, "Unable to gain focus! RSB may not work properly.");
+ }
+ }
+ }
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt(SAVE_CLICKED_POS, getCheckedItem());
}
@Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (requestCode == ADD_FILE_REQUEST_CODE && resultCode == RESULT_OK) {
+ // Add the custom ringtone in a separate thread
+ final AsyncTask<Uri, Void, Uri> installTask = new AsyncTask<Uri, Void, Uri>() {
+ @Override
+ protected Uri doInBackground(Uri... params) {
+ try {
+ return mRingtoneManager.addCustomExternalRingtone(params[0], mType);
+ } catch (IOException | IllegalArgumentException e) {
+ Log.e(TAG, "Unable to add new ringtone", e);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Uri ringtoneUri) {
+ if (ringtoneUri != null) {
+ requeryForAdapter();
+ } else {
+ // Ringtone was not added, display error Toast
+ Toast.makeText(RingtonePickerActivity.this, R.string.unable_to_add_ringtone,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ };
+ installTask.execute(data.getData());
+ }
+ }
+
+ // Disabled because context menus aren't Material Design :(
+ /*
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
+ int position = ((AdapterContextMenuInfo) menuInfo).position;
+
+ Ringtone ringtone = getRingtone(getRingtoneManagerPosition(position));
+ if (ringtone != null && mRingtoneManager.isCustomRingtone(ringtone.getUri())) {
+ // It's a custom ringtone so we display the context menu
+ menu.setHeaderTitle(ringtone.getTitle(this));
+ menu.add(Menu.NONE, Menu.FIRST, Menu.NONE, R.string.delete_ringtone_text);
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case Menu.FIRST: {
+ int deletedRingtonePos = ((AdapterContextMenuInfo) item.getMenuInfo()).position;
+ Uri deletedRingtoneUri = getRingtone(
+ getRingtoneManagerPosition(deletedRingtonePos)).getUri();
+ if(mRingtoneManager.deleteExternalRingtone(deletedRingtoneUri)) {
+ requeryForAdapter();
+ } else {
+ Toast.makeText(this, R.string.unable_to_delete_ringtone, Toast.LENGTH_SHORT)
+ .show();
+ }
+ return true;
+ }
+ default: {
+ return false;
+ }
+ }
+ }
+ */
+
+ @Override
public void onDestroy() {
- mRingtonePickerViewModel.cancelPendingAsyncTasks();
+ if (mHandler != null) {
+ mHandler.removeCallbacksAndMessages(null);
+ }
+ if (mCursor != null) {
+ mCursor.close();
+ mCursor = null;
+ }
super.onDestroy();
}
+ public void onPrepareListView(ListView listView) {
+ // Reset the static item count, as this method can be called multiple times
+ mStaticItemCount = 0;
+
+ if (mHasDefaultItem) {
+ mDefaultRingtonePos = addDefaultRingtoneItem(listView);
+
+ if (getCheckedItem() == POS_UNKNOWN && RingtoneManager.isDefault(mExistingUri)) {
+ setCheckedItem(mDefaultRingtonePos);
+ }
+ }
+
+ if (mHasSilentItem) {
+ mSilentPos = addSilentItem(listView);
+
+ // The 'Silent' item should use a null Uri
+ if (getCheckedItem() == POS_UNKNOWN && mExistingUri == null) {
+ setCheckedItem(mSilentPos);
+ }
+ }
+
+ if (getCheckedItem() == POS_UNKNOWN) {
+ setCheckedItem(getListPosition(mRingtoneManager.getRingtonePosition(mExistingUri)));
+ }
+
+ // In the buttonless (watch-only) version, preemptively set our result since we won't
+ // have another chance to do so before the activity closes.
+ if (!mShowOkCancelButtons) {
+ setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
+ }
+ // If external storage is available, add a button to install sounds from storage.
+ if (resolvesMediaFilePicker()
+ && Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ addNewSoundItem(listView);
+ }
+
+ // Enable context menu in ringtone items
+ registerForContextMenu(listView);
+ }
+
+ /**
+ * Re-query RingtoneManager for the most recent set of installed ringtones. May move the
+ * selected item position to match the new position of the chosen sound.
+ *
+ * This should only need to happen after adding or removing a ringtone.
+ */
+ private void requeryForAdapter() {
+ // Refresh and set a new cursor, closing the old one.
+ initRingtoneManager();
+ mAdapter.changeCursor(mCursor);
+
+ // Update checked item location.
+ int checkedPosition = POS_UNKNOWN;
+ for (int i = 0; i < mAdapter.getCount(); i++) {
+ if (mAdapter.getItemId(i) == mCheckedItemId) {
+ checkedPosition = getListPosition(i);
+ break;
+ }
+ }
+ if (mHasSilentItem && checkedPosition == POS_UNKNOWN) {
+ checkedPosition = mSilentPos;
+ }
+ setCheckedItem(checkedPosition);
+ setupAlert();
+ }
+
+ /**
+ * Adds a static item to the top of the list. A static item is one that is not from the
+ * RingtoneManager.
+ *
+ * @param listView The ListView to add to.
+ * @param textResId The resource ID of the text for the item.
+ * @return The position of the inserted item.
+ */
+ private int addStaticItem(ListView listView, int textResId) {
+ TextView textView = (TextView) getLayoutInflater().inflate(
+ com.android.internal.R.layout.select_dialog_singlechoice_material, listView, false);
+ textView.setText(textResId);
+ listView.addHeaderView(textView);
+ mStaticItemCount++;
+ return listView.getHeaderViewsCount() - 1;
+ }
+
+ private int addDefaultRingtoneItem(ListView listView) {
+ if (mType == RingtoneManager.TYPE_NOTIFICATION) {
+ return addStaticItem(listView, R.string.notification_sound_default);
+ } else if (mType == RingtoneManager.TYPE_ALARM) {
+ return addStaticItem(listView, R.string.alarm_sound_default);
+ }
+
+ return addStaticItem(listView, R.string.ringtone_default);
+ }
+
+ private int addSilentItem(ListView listView) {
+ return addStaticItem(listView, com.android.internal.R.string.ringtone_silent);
+ }
+
+ private void addNewSoundItem(ListView listView) {
+ View view = getLayoutInflater().inflate(R.layout.add_new_sound_item, listView,
+ false /* attachToRoot */);
+ TextView text = (TextView)view.findViewById(R.id.add_new_sound_text);
+
+ if (mType == RingtoneManager.TYPE_ALARM) {
+ text.setText(R.string.add_alarm_text);
+ } else if (mType == RingtoneManager.TYPE_NOTIFICATION) {
+ text.setText(R.string.add_notification_text);
+ } else {
+ text.setText(R.string.add_ringtone_text);
+ }
+ listView.addFooterView(view);
+ }
+
+ private void initRingtoneManager() {
+ // Reinstantiate the RingtoneManager. Cursor.requery() was deprecated and calling it
+ // causes unexpected behavior.
+ mRingtoneManager = new RingtoneManager(mTargetContext, /* includeParentRingtones */ true);
+ if (mType != -1) {
+ mRingtoneManager.setType(mType);
+ }
+ mCursor = new LocalizedCursor(mRingtoneManager.getCursor(), getResources(), COLUMN_LABEL);
+ }
+
+ private Ringtone getRingtone(int ringtoneManagerPosition) {
+ if (ringtoneManagerPosition < 0) {
+ return null;
+ }
+ return mRingtoneManager.getRingtone(ringtoneManagerPosition);
+ }
+
+ private int getCheckedItem() {
+ return mAlertParams.mCheckedItem;
+ }
+
+ private void setCheckedItem(int pos) {
+ mAlertParams.mCheckedItem = pos;
+ mCheckedItemId = mAdapter.getItemId(getRingtoneManagerPosition(pos));
+ }
+
+ /*
+ * On click of Ok/Cancel buttons
+ */
+ public void onClick(DialogInterface dialog, int which) {
+ boolean positiveResult = which == DialogInterface.BUTTON_POSITIVE;
+
+ // Stop playing the previous ringtone
+ mRingtoneManager.stopPreviousRingtone();
+
+ if (positiveResult) {
+ setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
+ } else {
+ setResult(RESULT_CANCELED);
+ }
+
+ finish();
+ }
+
+ /*
+ * On item selected via keys
+ */
+ public void onItemSelected(AdapterView parent, View view, int position, long id) {
+ // footer view
+ if (position >= mCursor.getCount() + mStaticItemCount) {
+ return;
+ }
+
+ playRingtone(position, DELAY_MS_SELECTION_PLAYED);
+
+ // In the buttonless (watch-only) version, preemptively set our result since we won't
+ // have another chance to do so before the activity closes.
+ if (!mShowOkCancelButtons) {
+ setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
+ }
+ }
+
+ public void onNothingSelected(AdapterView parent) {
+ }
+
+ private void playRingtone(int position, int delayMs) {
+ mHandler.removeCallbacks(this);
+ mSampleRingtonePos = position;
+ mHandler.postDelayed(this, delayMs);
+ }
+
+ public void run() {
+ stopAnyPlayingRingtone();
+ if (mSampleRingtonePos == mSilentPos) {
+ return;
+ }
+
+ Ringtone ringtone;
+ if (mSampleRingtonePos == mDefaultRingtonePos) {
+ if (mDefaultRingtone == null) {
+ mDefaultRingtone = RingtoneManager.getRingtone(this, mUriForDefaultItem);
+ }
+ /*
+ * Stream type of mDefaultRingtone is not set explicitly here.
+ * It should be set in accordance with mRingtoneManager of this Activity.
+ */
+ if (mDefaultRingtone != null) {
+ mDefaultRingtone.setStreamType(mRingtoneManager.inferStreamType());
+ }
+ ringtone = mDefaultRingtone;
+ mCurrentRingtone = null;
+ } else {
+ ringtone = mRingtoneManager.getRingtone(getRingtoneManagerPosition(mSampleRingtonePos));
+ mCurrentRingtone = ringtone;
+ }
+
+ if (ringtone != null) {
+ if (mAttributesFlags != 0) {
+ ringtone.setAudioAttributes(
+ new AudioAttributes.Builder(ringtone.getAudioAttributes())
+ .setFlags(mAttributesFlags)
+ .build());
+ }
+ ringtone.play();
+ }
+ }
+
@Override
protected void onStop() {
super.onStop();
- mRingtonePickerViewModel.onStop(isChangingConfigurations());
+
+ if (!isChangingConfigurations()) {
+ stopAnyPlayingRingtone();
+ } else {
+ saveAnyPlayingRingtone();
+ }
}
@Override
protected void onPause() {
super.onPause();
- mRingtonePickerViewModel.onPause(isChangingConfigurations());
- }
-
- /**
- * Maps the ringtone picker category to the appropriate PickerType.
- * If the category is null or the feature is still not released, then it defaults to sound
- * picker.
- *
- * @param category the ringtone picker category.
- * @return the corresponding picker type.
- */
- private static RingtonePickerViewModel.PickerType mapCategoryToPickerType(String category) {
- if (category == null || !RINGTONE_PICKER_CATEGORY_FEATURE_ENABLED) {
- return RingtonePickerViewModel.PickerType.SOUND_PICKER;
- }
-
- switch (category) {
- case "android.intent.category.RINGTONE_PICKER_RINGTONE":
- return RingtonePickerViewModel.PickerType.RINGTONE_PICKER;
- case "android.intent.category.RINGTONE_PICKER_SOUND":
- return RingtonePickerViewModel.PickerType.SOUND_PICKER;
- case "android.intent.category.RINGTONE_PICKER_VIBRATION":
- return RingtonePickerViewModel.PickerType.VIBRATION_PICKER;
- default:
- Log.w(TAG, "Unrecognized category: " + category + ". Defaulting to sound picker.");
- return RingtonePickerViewModel.PickerType.SOUND_PICKER;
+ if (!isChangingConfigurations()) {
+ stopAnyPlayingRingtone();
}
}
-}
+
+ private void setSuccessResultWithRingtone(Uri ringtoneUri) {
+ setResult(RESULT_OK,
+ new Intent().putExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, ringtoneUri));
+ }
+
+ private Uri getCurrentlySelectedRingtoneUri() {
+ if (getCheckedItem() == POS_UNKNOWN) {
+ // When the getCheckItem is POS_UNKNOWN, it is not the case we expected.
+ // We return null for this case.
+ return null;
+ } else if (getCheckedItem() == mDefaultRingtonePos) {
+ // Use the default Uri that they originally gave us.
+ return mUriForDefaultItem;
+ } else if (getCheckedItem() == mSilentPos) {
+ // Use a null Uri for the 'Silent' item.
+ return null;
+ } else {
+ return mRingtoneManager.getRingtoneUri(getRingtoneManagerPosition(getCheckedItem()));
+ }
+ }
+
+ private void saveAnyPlayingRingtone() {
+ if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) {
+ sPlayingRingtone = mDefaultRingtone;
+ } else if (mCurrentRingtone != null && mCurrentRingtone.isPlaying()) {
+ sPlayingRingtone = mCurrentRingtone;
+ }
+ }
+
+ private void stopAnyPlayingRingtone() {
+ if (sPlayingRingtone != null && sPlayingRingtone.isPlaying()) {
+ sPlayingRingtone.stop();
+ }
+ sPlayingRingtone = null;
+
+ if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) {
+ mDefaultRingtone.stop();
+ }
+
+ if (mRingtoneManager != null) {
+ mRingtoneManager.stopPreviousRingtone();
+ }
+ }
+
+ private int getRingtoneManagerPosition(int listPos) {
+ return listPos - mStaticItemCount;
+ }
+
+ private int getListPosition(int ringtoneManagerPos) {
+
+ // If the manager position is -1 (for not found), return that
+ if (ringtoneManagerPos < 0) return ringtoneManagerPos;
+
+ return ringtoneManagerPos + mStaticItemCount;
+ }
+
+ private Intent getMediaFilePickerIntent() {
+ final Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
+ chooseFile.setType("audio/*");
+ chooseFile.putExtra(Intent.EXTRA_MIME_TYPES,
+ new String[] { "audio/*", "application/ogg" });
+ return chooseFile;
+ }
+
+ private boolean resolvesMediaFilePicker() {
+ return getMediaFilePickerIntent().resolveActivity(getPackageManager()) != null;
+ }
+
+ private static class LocalizedCursor extends CursorWrapper {
+
+ final int mTitleIndex;
+ final Resources mResources;
+ String mNamePrefix;
+ final Pattern mSanitizePattern;
+
+ LocalizedCursor(Cursor cursor, Resources resources, String columnLabel) {
+ super(cursor);
+ mTitleIndex = mCursor.getColumnIndex(columnLabel);
+ mResources = resources;
+ mSanitizePattern = Pattern.compile("[^a-zA-Z0-9]");
+ if (mTitleIndex == -1) {
+ Log.e(TAG, "No index for column " + columnLabel);
+ mNamePrefix = null;
+ } else {
+ try {
+ // Build the prefix for the name of the resource to look up
+ // format is: "ResourcePackageName::ResourceTypeName/"
+ // (the type name is expected to be "string" but let's not hardcode it).
+ // Here we use an existing resource "notification_sound_default" which is
+ // always expected to be found.
+ mNamePrefix = String.format("%s:%s/%s",
+ mResources.getResourcePackageName(R.string.notification_sound_default),
+ mResources.getResourceTypeName(R.string.notification_sound_default),
+ SOUND_NAME_RES_PREFIX);
+ } catch (NotFoundException e) {
+ mNamePrefix = null;
+ }
+ }
+ }
+
+ /**
+ * Process resource name to generate a valid resource name.
+ * @param input
+ * @return a non-null String
+ */
+ private String sanitize(String input) {
+ if (input == null) {
+ return "";
+ }
+ return mSanitizePattern.matcher(input).replaceAll("_").toLowerCase();
+ }
+
+ @Override
+ public String getString(int columnIndex) {
+ final String defaultName = mCursor.getString(columnIndex);
+ if ((columnIndex != mTitleIndex) || (mNamePrefix == null)) {
+ return defaultName;
+ }
+ TypedValue value = new TypedValue();
+ try {
+ // the name currently in the database is used to derive a name to match
+ // against resource names in this package
+ mResources.getValue(mNamePrefix + sanitize(defaultName), value, false);
+ } catch (NotFoundException e) {
+ // no localized string, use the default string
+ return defaultName;
+ }
+ if ((value != null) && (value.type == TypedValue.TYPE_STRING)) {
+ Log.d(TAG, String.format("Replacing name %s with %s",
+ defaultName, value.string.toString()));
+ return value.string.toString();
+ } else {
+ Log.e(TAG, "Invalid value when looking up localized name, using " + defaultName);
+ return defaultName;
+ }
+ }
+ }
+
+ private class BadgedRingtoneAdapter extends CursorAdapter {
+ private final boolean mIsManagedProfile;
+
+ public BadgedRingtoneAdapter(Context context, Cursor cursor, boolean isManagedProfile) {
+ super(context, cursor);
+ mIsManagedProfile = isManagedProfile;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ if (position < 0) {
+ return position;
+ }
+ return super.getItemId(position);
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ LayoutInflater inflater = LayoutInflater.from(context);
+ return inflater.inflate(R.layout.radio_with_work_badge, parent, false);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ // Set text as the title of the ringtone
+ ((TextView) view.findViewById(R.id.checked_text_view))
+ .setText(cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX));
+
+ boolean isWorkRingtone = false;
+ if (mIsManagedProfile) {
+ /*
+ * Display the work icon if the ringtone belongs to a work profile. We can tell that
+ * a ringtone belongs to a work profile if the picker user is a managed profile, the
+ * ringtone Uri is in external storage, and either the uri has no user id or has the
+ * id of the picker user
+ */
+ Uri currentUri = mRingtoneManager.getRingtoneUri(cursor.getPosition());
+ int uriUserId = ContentProvider.getUserIdFromUri(currentUri, mPickerUserId);
+ Uri uriWithoutUserId = ContentProvider.getUriWithoutUserId(currentUri);
+
+ if (uriUserId == mPickerUserId && uriWithoutUserId.toString()
+ .startsWith(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI.toString())) {
+ isWorkRingtone = true;
+ }
+ }
+
+ ImageView workIcon = (ImageView) view.findViewById(R.id.work_icon);
+ if(isWorkRingtone) {
+ workIcon.setImageDrawable(getPackageManager().getUserBadgeForDensityNoBackground(
+ UserHandle.of(mPickerUserId), -1 /* density */));
+ workIcon.setVisibility(View.VISIBLE);
+ } else {
+ workIcon.setVisibility(View.GONE);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SoundPicker2/Android.bp b/packages/SoundPicker2/Android.bp
new file mode 100644
index 0000000..f4d8bf2
--- /dev/null
+++ b/packages/SoundPicker2/Android.bp
@@ -0,0 +1,46 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+ name: "SoundPicker2Lib",
+ srcs: [
+ "src/**/*.java",
+ ],
+ resource_dirs: [
+ "res",
+ ],
+ static_libs: [
+ "androidx.appcompat_appcompat",
+ "hilt_android",
+ "guava",
+ "androidx.recyclerview_recyclerview",
+ "androidx-constraintlayout_constraintlayout",
+ "androidx.viewpager2_viewpager2",
+ "com.google.android.material_material",
+ ],
+}
+
+android_app {
+ name: "SoundPicker2",
+ defaults: ["platform_app_defaults"],
+ manifest: "AndroidManifest.xml",
+ static_libs: ["SoundPicker2Lib"],
+ platform_apis: true,
+ certificate: "media",
+ privileged: true,
+
+ optimize: {
+ enabled: true,
+ optimize: true,
+ shrink: true,
+ shrink_resources: true,
+ obfuscate: false,
+ proguard_compatibility: false,
+ },
+}
diff --git a/packages/SoundPicker2/AndroidManifest.xml b/packages/SoundPicker2/AndroidManifest.xml
new file mode 100644
index 0000000..934b003
--- /dev/null
+++ b/packages/SoundPicker2/AndroidManifest.xml
@@ -0,0 +1,43 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.soundpicker"
+ android:sharedUserId="android.media">
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+ <uses-permission android:name="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY" />
+ <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+
+ <application
+ android:name=".RingtonePickerApplication"
+ android:allowBackup="false"
+ android:label="@string/app_label"
+ android:theme="@style/Theme.AppCompat"
+ android:supportsRtl="true">
+ <receiver android:name="RingtoneReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY"/>
+ </intent-filter>
+ </receiver>
+
+ <service android:name="RingtoneOverlayService" />
+
+ <activity android:name="RingtonePickerActivity"
+ android:theme="@style/Theme.AppCompat.Dialog"
+ android:enabled="@*android:bool/config_defaultRingtonePickerEnabled"
+ android:excludeFromRecents="true"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.RINGTONE_PICKER" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.RINGTONE_PICKER_SOUND" />
+ <category android:name="android.intent.category.RINGTONE_PICKER_VIBRATION" />
+ <category android:name="android.intent.category.RINGTONE_PICKER_RINGTONE" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/packages/SoundPicker2/OWNERS b/packages/SoundPicker2/OWNERS
new file mode 100644
index 0000000..5bf46e0
--- /dev/null
+++ b/packages/SoundPicker2/OWNERS
@@ -0,0 +1,2 @@
+# Haptics team works on the SoundPicker
+include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS
diff --git a/packages/SoundPicker2/res/drawable/ic_add.xml b/packages/SoundPicker2/res/drawable/ic_add.xml
new file mode 100644
index 0000000..22b3fe9
--- /dev/null
+++ b/packages/SoundPicker2/res/drawable/ic_add.xml
@@ -0,0 +1,24 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="?android:attr/colorAccent"
+ android:pathData="M38.0,26.0L26.0,26.0l0.0,12.0l-4.0,0.0L22.0,26.0L10.0,26.0l0.0,-4.0l12.0,0.0L22.0,10.0l4.0,0.0l0.0,12.0l12.0,0.0l0.0,4.0z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SoundPicker2/res/drawable/ic_add_padded.xml b/packages/SoundPicker2/res/drawable/ic_add_padded.xml
new file mode 100644
index 0000000..c376867
--- /dev/null
+++ b/packages/SoundPicker2/res/drawable/ic_add_padded.xml
@@ -0,0 +1,22 @@
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/ic_add"
+ android:insetTop="4dp"
+ android:insetRight="4dp"
+ android:insetBottom="4dp"
+ android:insetLeft="4dp"/>
diff --git a/packages/SoundPicker2/res/layout-watch/add_new_sound_item.xml b/packages/SoundPicker2/res/layout-watch/add_new_sound_item.xml
new file mode 100644
index 0000000..edfc0ab
--- /dev/null
+++ b/packages/SoundPicker2/res/layout-watch/add_new_sound_item.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+ Currently, no file manager app on watch could handle ACTION_GET_CONTENT intent.
+ Make the visibility to "gone" to prevent failures.
+ -->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/add_new_sound_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@null"
+ android:textColor="?android:attr/colorAccent"
+ android:gravity="center_vertical"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:drawableStart="@drawable/ic_add_padded"
+ android:drawablePadding="8dp"
+ android:ellipsize="marquee"
+ android:visibility="gone" />
diff --git a/packages/SoundPicker2/res/layout-watch/radio_with_work_badge.xml b/packages/SoundPicker2/res/layout-watch/radio_with_work_badge.xml
new file mode 100644
index 0000000..ee29a37
--- /dev/null
+++ b/packages/SoundPicker2/res/layout-watch/radio_with_work_badge.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.soundpicker.CheckedListItem xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackground"
+ >
+
+ <CheckedTextView
+ android:id="@+id/checked_text_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorAlertDialogListItem"
+ android:gravity="center_vertical"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:drawableStart="?android:attr/listChoiceIndicatorSingle"
+ android:drawablePadding="8dp"
+ android:ellipsize="marquee"
+ android:layout_toLeftOf="@+id/work_icon"
+ android:maxLines="3" />
+
+ <ImageView
+ android:id="@id/work_icon"
+ android:layout_width="18dp"
+ android:layout_height="18dp"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:scaleType="centerCrop"
+ android:layout_marginRight="20dp" />
+</com.android.soundpicker.CheckedListItem>
diff --git a/packages/SoundPicker/res/layout/activity_ringtone_picker.xml b/packages/SoundPicker2/res/layout/activity_ringtone_picker.xml
similarity index 100%
rename from packages/SoundPicker/res/layout/activity_ringtone_picker.xml
rename to packages/SoundPicker2/res/layout/activity_ringtone_picker.xml
diff --git a/packages/SoundPicker2/res/layout/add_new_sound_item.xml b/packages/SoundPicker2/res/layout/add_new_sound_item.xml
new file mode 100644
index 0000000..024b97e
--- /dev/null
+++ b/packages/SoundPicker2/res/layout/add_new_sound_item.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackground"
+ android:focusable="true"
+ android:clickable="true">
+
+ <ImageView
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:scaleType="centerCrop"
+ android:layout_marginRight="24dp"
+ android:layout_marginLeft="24dp"
+ android:src="@drawable/ic_add"/>
+
+ <TextView
+ android:id="@+id/add_new_sound_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:text="@null"
+ android:textColor="?android:attr/colorAccent"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:maxLines="3"
+ android:gravity="center_vertical"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:drawablePadding="20dp"
+ android:ellipsize="marquee"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SoundPicker/res/layout/fragment_ringtone_picker.xml b/packages/SoundPicker2/res/layout/fragment_ringtone_picker.xml
similarity index 100%
rename from packages/SoundPicker/res/layout/fragment_ringtone_picker.xml
rename to packages/SoundPicker2/res/layout/fragment_ringtone_picker.xml
diff --git a/packages/SoundPicker/res/layout/fragment_tabbed_dialog.xml b/packages/SoundPicker2/res/layout/fragment_tabbed_dialog.xml
similarity index 100%
rename from packages/SoundPicker/res/layout/fragment_tabbed_dialog.xml
rename to packages/SoundPicker2/res/layout/fragment_tabbed_dialog.xml
diff --git a/packages/SoundPicker2/res/layout/radio_with_work_badge.xml b/packages/SoundPicker2/res/layout/radio_with_work_badge.xml
new file mode 100644
index 0000000..36ac93e
--- /dev/null
+++ b/packages/SoundPicker2/res/layout/radio_with_work_badge.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<com.android.soundpicker.CheckedListItem
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackground"
+ android:focusable="true"
+ android:clickable="true">
+
+ <CheckedTextView
+ android:id="@+id/checked_text_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorAlertDialogListItem"
+ android:gravity="center_vertical"
+ android:paddingStart="20dp"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:drawableStart="?android:attr/listChoiceIndicatorSingle"
+ android:drawablePadding="20dp"
+ android:ellipsize="marquee"
+ android:layout_toLeftOf="@+id/work_icon"
+ android:maxLines="3"/>
+
+ <ImageView
+ android:id="@id/work_icon"
+ android:layout_width="18dp"
+ android:layout_height="18dp"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:scaleType="centerCrop"
+ android:layout_marginRight="20dp"/>
+</com.android.soundpicker.CheckedListItem>
diff --git a/packages/SoundPicker2/res/raw/default_alarm_alert.ogg b/packages/SoundPicker2/res/raw/default_alarm_alert.ogg
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/SoundPicker2/res/raw/default_alarm_alert.ogg
diff --git a/packages/SoundPicker2/res/raw/default_notification_sound.ogg b/packages/SoundPicker2/res/raw/default_notification_sound.ogg
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/SoundPicker2/res/raw/default_notification_sound.ogg
diff --git a/packages/SoundPicker2/res/raw/default_ringtone.ogg b/packages/SoundPicker2/res/raw/default_ringtone.ogg
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/SoundPicker2/res/raw/default_ringtone.ogg
diff --git a/packages/SoundPicker2/res/values/config.xml b/packages/SoundPicker2/res/values/config.xml
new file mode 100644
index 0000000..4e237a2
--- /dev/null
+++ b/packages/SoundPicker2/res/values/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. Do not translate.
+
+ NOTE: The naming convention is "config_camelCaseValue". -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- True if the ringtone picker should show the ok/cancel buttons. If it is not shown, the
+ ringtone will be automatically selected when the picker is closed. -->
+ <bool name="config_showOkCancelButtons">true</bool>
+</resources>
diff --git a/packages/SoundPicker2/res/values/strings.xml b/packages/SoundPicker2/res/values/strings.xml
new file mode 100644
index 0000000..ab7b95a
--- /dev/null
+++ b/packages/SoundPicker2/res/values/strings.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Choice in the ringtone picker. If chosen, the default ringtone will be used. -->
+ <string name="ringtone_default">Default ringtone</string>
+
+ <!-- Choice in the notification sound picker. If chosen, the default notification sound will be
+ used. -->
+ <string name="notification_sound_default">Default notification sound</string>
+
+ <!-- Choice in the alarm sound picker. If chosen, the default alarm sound will be used. -->
+ <string name="alarm_sound_default">Default alarm sound</string>
+
+ <!-- Text for the RingtonePicker item that allows adding a new ringtone. -->
+ <string name="add_ringtone_text">Add ringtone</string>
+ <!-- Text for the RingtonePicker item that allows adding a new alarm. -->
+ <string name="add_alarm_text">Add alarm</string>
+ <!-- Text for the RingtonePicker item that allows adding a new notification. -->
+ <string name="add_notification_text">Add notification</string>
+ <!-- Text for the RingtonePicker item ContextMenu that allows deleting a custom ringtone. -->
+ <string name="delete_ringtone_text">Delete</string>
+ <!-- Text for the Toast displayed when adding a custom ringtone fails. -->
+ <string name="unable_to_add_ringtone">Unable to add custom ringtone</string>
+ <!-- Text for the Toast displayed when deleting a custom ringtone fails. -->
+ <string name="unable_to_delete_ringtone">Unable to delete custom ringtone</string>
+
+ <!-- Text for the name of the app. [CHAR LIMIT=12] -->
+ <string name="app_label">Sounds</string>
+
+ <string name="empty_list">The list is empty</string>
+ <string name="sound_page_title">Sound</string>
+ <string name="vibration_page_title">Vibration</string>
+</resources>
diff --git a/packages/SoundPicker2/res/values/styles.xml b/packages/SoundPicker2/res/values/styles.xml
new file mode 100644
index 0000000..d22d9c4
--- /dev/null
+++ b/packages/SoundPicker2/res/values/styles.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <style name="PickerDialogTheme" parent="@*android:style/Theme.DeviceDefault.Settings.Dialog">
+ </style>
+
+</resources>
diff --git a/packages/SoundPicker/src/com/android/soundpicker/BasePickerFragment.java b/packages/SoundPicker2/src/com/android/soundpicker/BasePickerFragment.java
similarity index 100%
rename from packages/SoundPicker/src/com/android/soundpicker/BasePickerFragment.java
rename to packages/SoundPicker2/src/com/android/soundpicker/BasePickerFragment.java
diff --git a/packages/SoundPicker2/src/com/android/soundpicker/CheckedListItem.java b/packages/SoundPicker2/src/com/android/soundpicker/CheckedListItem.java
new file mode 100644
index 0000000..819ae98
--- /dev/null
+++ b/packages/SoundPicker2/src/com/android/soundpicker/CheckedListItem.java
@@ -0,0 +1,67 @@
+/*
+ * 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.soundpicker;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.Checkable;
+import android.widget.CheckedTextView;
+import android.widget.RelativeLayout;
+
+/**
+ * The {@link CheckedListItem} is a layout item that represents a ringtone, and is used in
+ * {@link RingtonePickerActivity}. It contains the ringtone's name, and a work badge to right of the
+ * name if the ringtone belongs to a work profile.
+ */
+public class CheckedListItem extends RelativeLayout implements Checkable {
+
+ public CheckedListItem(Context context) {
+ super(context);
+ }
+
+ public CheckedListItem(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public CheckedListItem(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public CheckedListItem(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ public void setChecked(boolean checked) {
+ getCheckedTextView().setChecked(checked);
+ }
+
+ @Override
+ public boolean isChecked() {
+ return getCheckedTextView().isChecked();
+ }
+
+ @Override
+ public void toggle() {
+ getCheckedTextView().toggle();
+ }
+
+ private CheckedTextView getCheckedTextView() {
+ return (CheckedTextView) findViewById(R.id.checked_text_view);
+ }
+
+}
diff --git a/packages/SoundPicker/src/com/android/soundpicker/ListeningExecutorServiceFactory.java b/packages/SoundPicker2/src/com/android/soundpicker/ListeningExecutorServiceFactory.java
similarity index 100%
rename from packages/SoundPicker/src/com/android/soundpicker/ListeningExecutorServiceFactory.java
rename to packages/SoundPicker2/src/com/android/soundpicker/ListeningExecutorServiceFactory.java
diff --git a/packages/SoundPicker/src/com/android/soundpicker/LocalizedCursor.java b/packages/SoundPicker2/src/com/android/soundpicker/LocalizedCursor.java
similarity index 100%
rename from packages/SoundPicker/src/com/android/soundpicker/LocalizedCursor.java
rename to packages/SoundPicker2/src/com/android/soundpicker/LocalizedCursor.java
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtoneFactory.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneFactory.java
similarity index 100%
rename from packages/SoundPicker/src/com/android/soundpicker/RingtoneFactory.java
rename to packages/SoundPicker2/src/com/android/soundpicker/RingtoneFactory.java
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtoneListHandler.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneListHandler.java
similarity index 100%
rename from packages/SoundPicker/src/com/android/soundpicker/RingtoneListHandler.java
rename to packages/SoundPicker2/src/com/android/soundpicker/RingtoneListHandler.java
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtoneListViewAdapter.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneListViewAdapter.java
similarity index 100%
rename from packages/SoundPicker/src/com/android/soundpicker/RingtoneListViewAdapter.java
rename to packages/SoundPicker2/src/com/android/soundpicker/RingtoneListViewAdapter.java
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtoneManagerFactory.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneManagerFactory.java
similarity index 100%
rename from packages/SoundPicker/src/com/android/soundpicker/RingtoneManagerFactory.java
rename to packages/SoundPicker2/src/com/android/soundpicker/RingtoneManagerFactory.java
diff --git a/packages/SoundPicker2/src/com/android/soundpicker/RingtoneOverlayService.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneOverlayService.java
new file mode 100644
index 0000000..b94ebeb
--- /dev/null
+++ b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneOverlayService.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.soundpicker;
+
+import android.app.Service;
+import android.content.Intent;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.IBinder;
+import android.provider.MediaStore;
+import android.provider.Settings.System;
+import android.util.Log;
+
+import androidx.annotation.IdRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Service to copy and set customization of default sounds
+ */
+public class RingtoneOverlayService extends Service {
+ private static final String TAG = "RingtoneOverlayService";
+ private static final boolean DEBUG = false;
+
+ @Override
+ public int onStartCommand(@Nullable final Intent intent, final int flags, final int startId) {
+ AsyncTask.execute(() -> {
+ updateRingtones();
+ stopSelf();
+ });
+
+ // Try again later if we are killed before we finish.
+ return Service.START_REDELIVER_INTENT;
+ }
+
+ @Override
+ public IBinder onBind(@Nullable final Intent intent) {
+ return null;
+ }
+
+ private void updateRingtones() {
+ copyResourceAndSetAsSound(R.raw.default_ringtone,
+ System.RINGTONE, Environment.DIRECTORY_RINGTONES);
+ copyResourceAndSetAsSound(R.raw.default_notification_sound,
+ System.NOTIFICATION_SOUND, Environment.DIRECTORY_NOTIFICATIONS);
+ copyResourceAndSetAsSound(R.raw.default_alarm_alert,
+ System.ALARM_ALERT, Environment.DIRECTORY_ALARMS);
+ }
+
+ /* If the resource contains any data, copy a resource to the file system, scan it, and set the
+ * file URI as the default for a sound. */
+ private void copyResourceAndSetAsSound(@IdRes final int id, @NonNull final String name,
+ @NonNull final String subPath) {
+ final File destDir = Environment.getExternalStoragePublicDirectory(subPath);
+ if (!destDir.exists() && !destDir.mkdirs()) {
+ Log.e(TAG, "can't create " + destDir.getAbsolutePath());
+ return;
+ }
+
+ final File dest = new File(destDir, "default_" + name + ".ogg");
+ try (
+ InputStream is = getResources().openRawResource(id);
+ FileOutputStream os = new FileOutputStream(dest);
+ ) {
+ if (is.available() > 0) {
+ FileUtils.copy(is, os);
+ final Uri uri = scanFile(dest);
+ if (uri != null) {
+ set(name, uri);
+ }
+ } else {
+ // TODO Shall we remove any former copied resource in this case and unset
+ // the defaults if we use this event a second time to clear the data?
+ if (DEBUG) Log.d(TAG, "Resource for " + name + " has no overlay");
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to open resource for " + name + ": " + e);
+ }
+ }
+
+ private Uri scanFile(@NonNull final File file) {
+ return MediaStore.scanFile(getContentResolver(), file);
+ }
+
+ private void set(@NonNull final String name, @NonNull final Uri uri) {
+ final Uri settingUri = System.getUriFor(name);
+ RingtoneManager.setActualDefaultRingtoneUri(this,
+ RingtoneManager.getDefaultType(settingUri), uri);
+ System.putInt(getContentResolver(), name + "_set", 1);
+ }
+}
diff --git a/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerActivity.java
new file mode 100644
index 0000000..90a14f9
--- /dev/null
+++ b/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerActivity.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2007 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.soundpicker;
+
+import android.content.Intent;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.util.Log;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentTransaction;
+import androidx.lifecycle.ViewModelProvider;
+
+import dagger.hilt.android.AndroidEntryPoint;
+
+/**
+ * The {@link RingtonePickerActivity} allows the user to choose one from all of the
+ * available ringtones. The chosen ringtone's URI will be persisted as a string.
+ *
+ * @see RingtoneManager#ACTION_RINGTONE_PICKER
+ */
+@AndroidEntryPoint(AppCompatActivity.class)
+public final class RingtonePickerActivity extends Hilt_RingtonePickerActivity {
+
+ private static final String TAG = "RingtonePickerActivity";
+ // TODO: Use the extra keys from RingtoneManager once they're added.
+ private static final String EXTRA_RINGTONE_PICKER_CATEGORY = "EXTRA_RINGTONE_PICKER_CATEGORY";
+ private static final String EXTRA_VIBRATION_SHOW_DEFAULT = "EXTRA_VIBRATION_SHOW_DEFAULT";
+ private static final String EXTRA_VIBRATION_DEFAULT_URI = "EXTRA_VIBRATION_DEFAULT_URI";
+ private static final String EXTRA_VIBRATION_SHOW_SILENT = "EXTRA_VIBRATION_SHOW_SILENT";
+ private static final String EXTRA_VIBRATION_EXISTING_URI = "EXTRA_VIBRATION_EXISTING_URI";
+ private static final boolean RINGTONE_PICKER_CATEGORY_FEATURE_ENABLED = false;
+
+ private RingtonePickerViewModel mRingtonePickerViewModel;
+ private int mAttributesFlags;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_ringtone_picker);
+
+ mRingtonePickerViewModel = new ViewModelProvider(this).get(RingtonePickerViewModel.class);
+
+ Intent intent = getIntent();
+ /**
+ * Id of the user to which the ringtone picker should list the ringtones
+ */
+ int pickerUserId = UserHandle.myUserId();
+
+ // Get the types of ringtones to show
+ int ringtoneType = intent.getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE,
+ RingtonePickerViewModel.RINGTONE_TYPE_UNKNOWN);
+
+ // AudioAttributes flags
+ mAttributesFlags |= intent.getIntExtra(
+ RingtoneManager.EXTRA_RINGTONE_AUDIO_ATTRIBUTES_FLAGS,
+ 0 /*defaultValue == no flags*/);
+
+ boolean showOkCancelButtons = getResources().getBoolean(R.bool.config_showOkCancelButtons);
+
+ String title = intent.getStringExtra(RingtoneManager.EXTRA_RINGTONE_TITLE);
+ if (title == null) {
+ title = getString(RingtonePickerViewModel.getTitleByType(ringtoneType));
+ }
+ String ringtonePickerCategory = intent.getStringExtra(EXTRA_RINGTONE_PICKER_CATEGORY);
+ RingtonePickerViewModel.PickerType pickerType = mapCategoryToPickerType(
+ ringtonePickerCategory);
+
+ RingtoneListHandler.Config soundListConfig = getSoundListConfig(pickerType, intent,
+ ringtoneType);
+ RingtoneListHandler.Config vibrationListConfig = getVibrationListConfig(pickerType, intent);
+
+ RingtonePickerViewModel.Config pickerConfig =
+ new RingtonePickerViewModel.Config(title, pickerUserId, ringtoneType,
+ showOkCancelButtons, mAttributesFlags, pickerType);
+
+ mRingtonePickerViewModel.init(pickerConfig, soundListConfig, vibrationListConfig);
+
+ if (savedInstanceState == null) {
+ TabbedDialogFragment dialogFragment = new TabbedDialogFragment();
+
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ Fragment prev = getSupportFragmentManager().findFragmentByTag(TabbedDialogFragment.TAG);
+ if (prev != null) {
+ ft.remove(prev);
+ }
+ ft.addToBackStack(null);
+ dialogFragment.show(ft, TabbedDialogFragment.TAG);
+ }
+
+ // The volume keys will control the stream that we are choosing a ringtone for
+ setVolumeControlStream(mRingtonePickerViewModel.getRingtoneStreamType());
+ }
+
+ private RingtoneListHandler.Config getSoundListConfig(
+ RingtonePickerViewModel.PickerType pickerType, Intent intent, int ringtoneType) {
+ if (pickerType != RingtonePickerViewModel.PickerType.SOUND_PICKER
+ && pickerType != RingtonePickerViewModel.PickerType.RINGTONE_PICKER) {
+ // This ringtone picker does not require a sound picker.
+ return null;
+ }
+
+ // Get whether to show the 'Default' sound item, and the URI to play when it's clicked
+ boolean hasDefaultSoundItem =
+ intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
+
+ // The Uri to play when the 'Default' sound item is clicked.
+ Uri uriForDefaultSoundItem =
+ intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI);
+ if (uriForDefaultSoundItem == null) {
+ uriForDefaultSoundItem = RingtonePickerViewModel.getDefaultItemUriByType(ringtoneType);
+ }
+
+ // Get whether this list has the 'Silent' sound item.
+ boolean hasSilentSoundItem =
+ intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
+
+ // AudioAttributes flags
+ mAttributesFlags |= intent.getIntExtra(
+ RingtoneManager.EXTRA_RINGTONE_AUDIO_ATTRIBUTES_FLAGS,
+ 0 /*defaultValue == no flags*/);
+
+ // Get the sound URI whose list item should have a checkmark
+ Uri existingSoundUri = intent
+ .getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI);
+
+ return new RingtoneListHandler.Config(hasDefaultSoundItem,
+ uriForDefaultSoundItem, hasSilentSoundItem, existingSoundUri);
+ }
+
+ private RingtoneListHandler.Config getVibrationListConfig(
+ RingtonePickerViewModel.PickerType pickerType, Intent intent) {
+ if (pickerType != RingtonePickerViewModel.PickerType.VIBRATION_PICKER
+ && pickerType != RingtonePickerViewModel.PickerType.RINGTONE_PICKER) {
+ // This ringtone picker does not require a vibration picker.
+ return null;
+ }
+
+ // Get whether to show the 'Default' vibration item, and the URI to play when it's clicked
+ boolean hasDefaultVibrationItem =
+ intent.getBooleanExtra(EXTRA_VIBRATION_SHOW_DEFAULT, false);
+
+ // The Uri to play when the 'Default' vibration item is clicked.
+ Uri uriForDefaultVibrationItem = intent.getParcelableExtra(EXTRA_VIBRATION_DEFAULT_URI);
+
+ // Get whether this list has the 'Silent' vibration item.
+ boolean hasSilentVibrationItem =
+ intent.getBooleanExtra(EXTRA_VIBRATION_SHOW_SILENT, true);
+
+ // Get the vibration URI whose list item should have a checkmark
+ Uri existingVibrationUri = intent.getParcelableExtra(EXTRA_VIBRATION_EXISTING_URI);
+
+ return new RingtoneListHandler.Config(
+ hasDefaultVibrationItem, uriForDefaultVibrationItem, hasSilentVibrationItem,
+ existingVibrationUri);
+ }
+
+ @Override
+ public void onDestroy() {
+ mRingtonePickerViewModel.cancelPendingAsyncTasks();
+ super.onDestroy();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mRingtonePickerViewModel.onStop(isChangingConfigurations());
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mRingtonePickerViewModel.onPause(isChangingConfigurations());
+ }
+
+ /**
+ * Maps the ringtone picker category to the appropriate PickerType.
+ * If the category is null or the feature is still not released, then it defaults to sound
+ * picker.
+ *
+ * @param category the ringtone picker category.
+ * @return the corresponding picker type.
+ */
+ private static RingtonePickerViewModel.PickerType mapCategoryToPickerType(String category) {
+ if (category == null || !RINGTONE_PICKER_CATEGORY_FEATURE_ENABLED) {
+ return RingtonePickerViewModel.PickerType.SOUND_PICKER;
+ }
+
+ switch (category) {
+ case "android.intent.category.RINGTONE_PICKER_RINGTONE":
+ return RingtonePickerViewModel.PickerType.RINGTONE_PICKER;
+ case "android.intent.category.RINGTONE_PICKER_SOUND":
+ return RingtonePickerViewModel.PickerType.SOUND_PICKER;
+ case "android.intent.category.RINGTONE_PICKER_VIBRATION":
+ return RingtonePickerViewModel.PickerType.VIBRATION_PICKER;
+ default:
+ Log.w(TAG, "Unrecognized category: " + category + ". Defaulting to sound picker.");
+ return RingtonePickerViewModel.PickerType.SOUND_PICKER;
+ }
+ }
+}
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerApplication.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerApplication.java
similarity index 100%
rename from packages/SoundPicker/src/com/android/soundpicker/RingtonePickerApplication.java
rename to packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerApplication.java
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerViewModel.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerViewModel.java
similarity index 100%
rename from packages/SoundPicker/src/com/android/soundpicker/RingtonePickerViewModel.java
rename to packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerViewModel.java
diff --git a/packages/SoundPicker2/src/com/android/soundpicker/RingtoneReceiver.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneReceiver.java
new file mode 100644
index 0000000..6a34936
--- /dev/null
+++ b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneReceiver.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2007 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.soundpicker;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class RingtoneReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_DEVICE_CUSTOMIZATION_READY.equals(action)) {
+ initResourceRingtones(context);
+ }
+ }
+
+ private void initResourceRingtones(Context context) {
+ context.startService(
+ new Intent(context, RingtoneOverlayService.class));
+ }
+}
diff --git a/packages/SoundPicker/src/com/android/soundpicker/SoundPickerFragment.java b/packages/SoundPicker2/src/com/android/soundpicker/SoundPickerFragment.java
similarity index 100%
rename from packages/SoundPicker/src/com/android/soundpicker/SoundPickerFragment.java
rename to packages/SoundPicker2/src/com/android/soundpicker/SoundPickerFragment.java
diff --git a/packages/SoundPicker/src/com/android/soundpicker/TabbedDialogFragment.java b/packages/SoundPicker2/src/com/android/soundpicker/TabbedDialogFragment.java
similarity index 100%
rename from packages/SoundPicker/src/com/android/soundpicker/TabbedDialogFragment.java
rename to packages/SoundPicker2/src/com/android/soundpicker/TabbedDialogFragment.java
diff --git a/packages/SoundPicker/src/com/android/soundpicker/VibrationPickerFragment.java b/packages/SoundPicker2/src/com/android/soundpicker/VibrationPickerFragment.java
similarity index 100%
rename from packages/SoundPicker/src/com/android/soundpicker/VibrationPickerFragment.java
rename to packages/SoundPicker2/src/com/android/soundpicker/VibrationPickerFragment.java
diff --git a/packages/SoundPicker/src/com/android/soundpicker/ViewPagerAdapter.java b/packages/SoundPicker2/src/com/android/soundpicker/ViewPagerAdapter.java
similarity index 100%
rename from packages/SoundPicker/src/com/android/soundpicker/ViewPagerAdapter.java
rename to packages/SoundPicker2/src/com/android/soundpicker/ViewPagerAdapter.java
diff --git a/packages/SoundPicker/tests/Android.bp b/packages/SoundPicker2/tests/Android.bp
similarity index 94%
rename from packages/SoundPicker/tests/Android.bp
rename to packages/SoundPicker2/tests/Android.bp
index c38426f..d88d442 100644
--- a/packages/SoundPicker/tests/Android.bp
+++ b/packages/SoundPicker2/tests/Android.bp
@@ -17,7 +17,7 @@
}
android_test {
- name: "SoundPickerTests",
+ name: "SoundPicker2Tests",
certificate: "platform",
libs: [
"android.test.runner",
@@ -30,7 +30,7 @@
"androidx.test.ext.truth",
"mockito-target-minus-junit4",
"guava-android-testlib",
- "SoundPickerLib",
+ "SoundPicker2Lib",
],
srcs: [
"src/**/*.java",
diff --git a/packages/SoundPicker/tests/AndroidManifest.xml b/packages/SoundPicker2/tests/AndroidManifest.xml
similarity index 100%
rename from packages/SoundPicker/tests/AndroidManifest.xml
rename to packages/SoundPicker2/tests/AndroidManifest.xml
diff --git a/packages/SoundPicker/tests/src/com/android/soundpicker/RingtoneListHandlerTest.java b/packages/SoundPicker2/tests/src/com/android/soundpicker/RingtoneListHandlerTest.java
similarity index 100%
rename from packages/SoundPicker/tests/src/com/android/soundpicker/RingtoneListHandlerTest.java
rename to packages/SoundPicker2/tests/src/com/android/soundpicker/RingtoneListHandlerTest.java
diff --git a/packages/SoundPicker/tests/src/com/android/soundpicker/RingtonePickerViewModelTest.java b/packages/SoundPicker2/tests/src/com/android/soundpicker/RingtonePickerViewModelTest.java
similarity index 100%
rename from packages/SoundPicker/tests/src/com/android/soundpicker/RingtonePickerViewModelTest.java
rename to packages/SoundPicker2/tests/src/com/android/soundpicker/RingtonePickerViewModelTest.java
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 03f7c99..de73b77 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -32,22 +32,6 @@
]
},
{
- "name": "SystemUIGoogleScreenshotTests",
- "options": [
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "android.platform.test.annotations.Postsubmit"
- }
- ],
- // The test doesn't run on AOSP Cuttlefish
- "keywords": ["internal"]
- },
- {
// TODO(b/251476085): Consider merging with SystemUIGoogleScreenshotTests (in U+)
"name": "SystemUIGoogleBiometricsScreenshotTests",
"options": [
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 2509cfd..211af90 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -29,3 +29,10 @@
"Notification Manager Service"
bug: "299448097"
}
+
+flag {
+ name: "scene_container"
+ namespace: "systemui"
+ description: "Enables the scene container framework go/flexiglass."
+ bug: "283121968"
+}
diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/CommunalGridLayout.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/CommunalGridLayout.kt
index 4ed78b3..33024f7 100644
--- a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/CommunalGridLayout.kt
+++ b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/CommunalGridLayout.kt
@@ -16,6 +16,7 @@
package com.android.systemui.communal.layout.ui.compose
+import android.util.SizeF
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -54,7 +55,14 @@
Row(
modifier = Modifier.height(layoutConfig.cardHeight(cardInfo.size)),
) {
- cardInfo.card.Content(Modifier.fillMaxSize())
+ cardInfo.card.Content(
+ modifier = Modifier.fillMaxSize(),
+ size =
+ SizeF(
+ layoutConfig.cardWidth.value,
+ layoutConfig.cardHeight(cardInfo.size).value,
+ ),
+ )
}
}
}
diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutCard.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutCard.kt
index ac8aa67..4b2a156 100644
--- a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutCard.kt
+++ b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutCard.kt
@@ -16,6 +16,7 @@
package com.android.systemui.communal.layout.ui.compose.config
+import android.util.SizeF
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -26,8 +27,11 @@
*
* To host non-Compose views, see
* https://developer.android.com/jetpack/compose/migrate/interoperability-apis/views-in-compose.
+ *
+ * @param size The size given to the card. Content of the card should fill all this space, given
+ * that margins and paddings have been taken care of by the layout.
*/
- @Composable abstract fun Content(modifier: Modifier)
+ @Composable abstract fun Content(modifier: Modifier, size: SizeF)
/**
* Sizes supported by the card.
diff --git a/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/CommunalLayoutEngineTest.kt b/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/CommunalLayoutEngineTest.kt
index fdf65f5..c1974ca 100644
--- a/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/CommunalLayoutEngineTest.kt
+++ b/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/CommunalLayoutEngineTest.kt
@@ -1,5 +1,6 @@
package com.android.systemui.communal.layout
+import android.util.SizeF
import androidx.compose.material3.Card
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -91,7 +92,7 @@
override val supportedSizes = listOf(size)
@Composable
- override fun Content(modifier: Modifier) {
+ override fun Content(modifier: Modifier, size: SizeF) {
Card(modifier = modifier, content = {})
}
}
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
index c6e429a..ddd1c67 100644
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
+++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
@@ -72,6 +72,10 @@
throwComposeUnavailableError()
}
+ override fun createCommunalContainer(context: Context, viewModel: CommunalViewModel): View {
+ throwComposeUnavailableError()
+ }
+
private fun throwComposeUnavailableError(): Nothing {
error(
"Compose is not available. Make sure to check isComposeAvailable() before calling any" +
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
index 1722685..eeda6c6 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
@@ -30,6 +30,7 @@
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.DisplayCutout
import com.android.systemui.common.ui.compose.windowinsets.DisplayCutoutProvider
+import com.android.systemui.communal.ui.compose.CommunalContainer
import com.android.systemui.communal.ui.compose.CommunalHub
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.people.ui.compose.PeopleScreen
@@ -104,6 +105,12 @@
}
}
+ override fun createCommunalContainer(context: Context, viewModel: CommunalViewModel): View {
+ return ComposeView(context).apply {
+ setContent { PlatformTheme { CommunalContainer(viewModel = viewModel) } }
+ }
+ }
+
// TODO(b/298525212): remove once Compose exposes window inset bounds.
private fun displayCutoutFromWindowInsets(
scope: CoroutineScope,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
new file mode 100644
index 0000000..46d418a
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -0,0 +1,123 @@
+package com.android.systemui.communal.ui.compose
+
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.SceneTransitionLayout
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.transitions
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+
+object Scenes {
+ val Blank = SceneKey(name = "blank")
+ val Communal = SceneKey(name = "communal")
+}
+
+object Communal {
+ object Elements {
+ val Content = ElementKey("CommunalContent")
+ }
+}
+
+val sceneTransitions = transitions {
+ from(Scenes.Blank, to = Scenes.Communal) {
+ spec = tween(durationMillis = 500)
+
+ translate(Communal.Elements.Content, Edge.Right)
+ fade(Communal.Elements.Content)
+ }
+}
+
+/**
+ * View containing a [SceneTransitionLayout] that shows the communal UI and handles transitions.
+ *
+ * This is a temporary container to allow the communal UI to use [SceneTransitionLayout] for gesture
+ * handling and transitions before the full Flexiglass layout is ready.
+ */
+@Composable
+fun CommunalContainer(modifier: Modifier = Modifier, viewModel: CommunalViewModel) {
+ val (currentScene, setCurrentScene) = remember { mutableStateOf(Scenes.Blank) }
+
+ // Failsafe to hide the whole SceneTransitionLayout in case of bugginess.
+ var showSceneTransitionLayout by remember { mutableStateOf(true) }
+ if (!showSceneTransitionLayout) {
+ return
+ }
+
+ SceneTransitionLayout(
+ modifier = modifier.fillMaxSize(),
+ currentScene = currentScene,
+ onChangeScene = setCurrentScene,
+ transitions = sceneTransitions,
+ ) {
+ scene(Scenes.Blank, userActions = mapOf(Swipe.Left to Scenes.Communal)) {
+ BlankScene { showSceneTransitionLayout = false }
+ }
+
+ scene(
+ Scenes.Communal,
+ userActions = mapOf(Swipe.Right to Scenes.Blank),
+ ) {
+ CommunalScene(viewModel, modifier = modifier)
+ }
+ }
+}
+
+/**
+ * Blank scene that shows over keyguard/dream. This scene will eventually show nothing at all and is
+ * only used to allow for transitions to the communal scene.
+ */
+@Composable
+private fun BlankScene(
+ modifier: Modifier = Modifier,
+ hideSceneTransitionLayout: () -> Unit,
+) {
+ Box(modifier.fillMaxSize()) {
+ Column(
+ Modifier.fillMaxHeight()
+ .width(100.dp)
+ .align(Alignment.CenterEnd)
+ .background(Color(0x55e9f2eb)),
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Text("Default scene")
+
+ IconButton(onClick = hideSceneTransitionLayout) {
+ Icon(Icons.Filled.Close, contentDescription = "Close button")
+ }
+ }
+ }
+}
+
+/** Scene containing the glanceable hub UI. */
+@Composable
+private fun SceneScope.CommunalScene(
+ viewModel: CommunalViewModel,
+ modifier: Modifier = Modifier,
+) {
+ Box(modifier.element(Communal.Elements.Content)) { CommunalHub(viewModel = viewModel) }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 3d827fb..b8fb264 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -1,5 +1,8 @@
package com.android.systemui.communal.ui.compose
+import android.appwidget.AppWidgetHostView
+import android.os.Bundle
+import android.util.SizeF
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
@@ -12,9 +15,12 @@
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.integerResource
+import androidx.compose.ui.viewinterop.AndroidView
import com.android.systemui.communal.layout.ui.compose.CommunalGridLayout
import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard
import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutConfig
+import com.android.systemui.communal.shared.model.CommunalContentSize
+import com.android.systemui.communal.ui.model.CommunalContentUiModel
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.res.R
@@ -24,6 +30,7 @@
viewModel: CommunalViewModel,
) {
val showTutorial by viewModel.showTutorialContent.collectAsState(initial = false)
+ val widgetContent by viewModel.widgetContent.collectAsState(initial = emptyList())
Box(
modifier = modifier.fillMaxSize().background(Color.White),
) {
@@ -36,7 +43,7 @@
gridHeight = dimensionResource(R.dimen.communal_grid_height),
gridColumnsPerCard = integerResource(R.integer.communal_grid_columns_per_card),
),
- communalCards = if (showTutorial) tutorialContent else emptyList(),
+ communalCards = if (showTutorial) tutorialContent else widgetContent.map(::contentCard),
)
}
}
@@ -58,8 +65,37 @@
override val supportedSizes = listOf(size)
@Composable
- override fun Content(modifier: Modifier) {
+ override fun Content(modifier: Modifier, size: SizeF) {
Card(modifier = modifier, content = {})
}
}
}
+
+private fun contentCard(model: CommunalContentUiModel): CommunalGridLayoutCard {
+ return object : CommunalGridLayoutCard() {
+ override val supportedSizes = listOf(convertToCardSize(model.size))
+ override val priority = model.priority
+
+ @Composable
+ override fun Content(modifier: Modifier, size: SizeF) {
+ AndroidView(
+ modifier = modifier,
+ factory = {
+ model.view.apply {
+ if (this is AppWidgetHostView) {
+ updateAppWidgetSize(Bundle(), listOf(size))
+ }
+ }
+ },
+ )
+ }
+ }
+}
+
+private fun convertToCardSize(size: CommunalContentSize): CommunalGridLayoutCard.Size {
+ return when (size) {
+ CommunalContentSize.FULL -> CommunalGridLayoutCard.Size.FULL
+ CommunalContentSize.HALF -> CommunalGridLayoutCard.Size.HALF
+ CommunalContentSize.THIRD -> CommunalGridLayoutCard.Size.THIRD
+ }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt b/packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt
index 27f0948..790665a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt
@@ -116,9 +116,14 @@
if (sizeCache.rowHeights.size != rows) {
sizeCache.rowHeights = IntArray(rows) { 0 }
+ } else {
+ repeat(rows) { i -> sizeCache.rowHeights[i] = 0 }
}
+
if (sizeCache.columnWidths.size != columns) {
sizeCache.columnWidths = IntArray(columns) { 0 }
+ } else {
+ repeat(columns) { i -> sizeCache.columnWidths[i] = 0 }
}
val totalHorizontalSpacingBetweenChildren =
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index acee425..8957903 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -83,6 +83,12 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
+ <!-- Placeholder for the communal UI that will be replaced if the feature is enabled. -->
+ <ViewStub
+ android:id="@+id/communal_ui_stub"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
<include layout="@layout/brightness_mirror_container" />
<com.android.systemui.scrim.ScrimView
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 9ac1e9f..572f6ff 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -531,19 +531,25 @@
</string>
<!-- A path similar to frameworks/base/core/res/res/values/config.xml
- config_mainBuiltInDisplayCutout that describes a path larger than the exact path of a display
- cutout. If present as well as config_enableDisplayCutoutProtection is set to true, then
- SystemUI will draw this "protection path" instead of the display cutout path that is normally
- used for anti-aliasing.
+ config_mainBuiltInDisplayCutout that describes a path larger than the exact path of a outer
+ display cutout. If present as well as config_enableDisplayCutoutProtection is set to true,
+ then SystemUI will draw this "protection path" instead of the display cutout path that is
+ normally used for anti-aliasing.
This path will only be drawn when the front-facing camera turns on, otherwise the main
DisplayCutout path will be rendered
-->
<string translatable="false" name="config_frontBuiltInDisplayCutoutProtection"></string>
- <!-- ID for the camera that needs extra protection -->
+ <!-- ID for the camera of outer display that needs extra protection -->
<string translatable="false" name="config_protectedCameraId"></string>
+ <!-- Similar to config_frontBuiltInDisplayCutoutProtection but for inner display. -->
+ <string translatable="false" name="config_innerBuiltInDisplayCutoutProtection"></string>
+
+ <!-- ID for the camera of inner display that needs extra protection -->
+ <string translatable="false" name="config_protectedInnerCameraId"></string>
+
<!-- Comma-separated list of packages to exclude from camera protection e.g.
"com.android.systemui,com.android.xyz" -->
<string translatable="false" name="config_cameraProtectionExcludedPackages"></string>
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModalities.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModalities.kt
index db46ccf..80f70a0 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModalities.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModalities.kt
@@ -33,6 +33,10 @@
val hasFingerprint: Boolean
get() = fingerprintProperties != null
+ /** If SFPS authentication is available. */
+ val hasSfps: Boolean
+ get() = hasFingerprint && fingerprintProperties!!.isAnySidefpsType
+
/** If fingerprint authentication is available (and [faceProperties] is non-null). */
val hasFace: Boolean
get() = faceProperties != null
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 1e89614..400f652 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -702,13 +702,18 @@
@Override
public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) {
- // Only hide the icon if the top task changes its requestedOrientation
- // Launcher can alter its requestedOrientation while it's not on top, don't hide on this
- Optional.ofNullable(ActivityManagerWrapper.getInstance())
- .map(ActivityManagerWrapper::getRunningTask)
- .ifPresent(a -> {
- if (a.id == taskId) setRotateSuggestionButtonState(false /* visible */);
- });
+ mBgExecutor.execute(() -> {
+ // Only hide the icon if the top task changes its requestedOrientation Launcher can
+ // alter its requestedOrientation while it's not on top, don't hide on this
+ Optional.ofNullable(ActivityManagerWrapper.getInstance())
+ .map(ActivityManagerWrapper::getRunningTask)
+ .ifPresent(a -> {
+ if (a.id == taskId) {
+ mMainThreadHandler.post(() ->
+ setRotateSuggestionButtonState(false /* visible */));
+ }
+ });
+ });
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
index eb20669..c505bd5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
@@ -61,6 +61,8 @@
InteractionJankMonitor.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS;
public static final int CUJ_OPEN_SEARCH_RESULT =
InteractionJankMonitor.CUJ_LAUNCHER_OPEN_SEARCH_RESULT;
+ public static final int CUJ_LAUNCHER_UNFOLD_ANIM =
+ InteractionJankMonitor.CUJ_LAUNCHER_UNFOLD_ANIM;
@IntDef({
CUJ_APP_LAUNCH_FROM_RECENTS,
@@ -77,6 +79,7 @@
CUJ_CLOSE_ALL_APPS_SWIPE,
CUJ_CLOSE_ALL_APPS_TO_HOME,
CUJ_OPEN_SEARCH_RESULT,
+ CUJ_LAUNCHER_UNFOLD_ANIM,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
index c4f1ce8..b186018 100644
--- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
@@ -33,11 +33,11 @@
import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD
import android.util.Log
-import com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.settings.SecureSettings
import java.io.PrintWriter
import javax.inject.Inject
@@ -50,6 +50,7 @@
@Main private val handler: Handler,
private val secureSettings: SecureSettings,
private val contentResolver: ContentResolver,
+ private val selectedUserInteractor: SelectedUserInteractor,
dumpManager: DumpManager
) : Dumpable {
@@ -134,7 +135,7 @@
)
)
- onChange(true, ArrayList(), 0, getCurrentUser())
+ onChange(true, ArrayList(), 0, selectedUserInteractor.getSelectedUserId())
}
private fun registerUri(uris: Collection<Uri>) {
@@ -153,29 +154,31 @@
flags: Int,
userId: Int
) {
- if (getCurrentUser() != userId) {
+ if (selectedUserInteractor.getSelectedUserId() != userId) {
return
}
if (selfChange || uris.contains(wakeUri)) {
requestActiveUnlockOnWakeup = secureSettings.getIntForUser(
- ACTIVE_UNLOCK_ON_WAKE, 0, getCurrentUser()) == 1
+ ACTIVE_UNLOCK_ON_WAKE, 0, selectedUserInteractor.getSelectedUserId()) == 1
}
if (selfChange || uris.contains(unlockIntentUri)) {
requestActiveUnlockOnUnlockIntent = secureSettings.getIntForUser(
- ACTIVE_UNLOCK_ON_UNLOCK_INTENT, 0, getCurrentUser()) == 1
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT, 0,
+ selectedUserInteractor.getSelectedUserId()) == 1
}
if (selfChange || uris.contains(bioFailUri)) {
requestActiveUnlockOnBioFail = secureSettings.getIntForUser(
- ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 0, getCurrentUser()) == 1
+ ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 0,
+ selectedUserInteractor.getSelectedUserId()) == 1
}
if (selfChange || uris.contains(faceErrorsUri)) {
processStringArray(
secureSettings.getStringForUser(ACTIVE_UNLOCK_ON_FACE_ERRORS,
- getCurrentUser()),
+ selectedUserInteractor.getSelectedUserId()),
faceErrorsToTriggerBiometricFailOn,
setOf(FACE_ERROR_TIMEOUT))
}
@@ -183,7 +186,7 @@
if (selfChange || uris.contains(faceAcquireInfoUri)) {
processStringArray(
secureSettings.getStringForUser(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
- getCurrentUser()),
+ selectedUserInteractor.getSelectedUserId()),
faceAcquireInfoToTriggerBiometricFailOn,
emptySet())
}
@@ -192,7 +195,7 @@
processStringArray(
secureSettings.getStringForUser(
ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
- getCurrentUser()),
+ selectedUserInteractor.getSelectedUserId()),
onUnlockIntentWhenBiometricEnrolled,
setOf(BiometricType.NONE.intValue))
}
@@ -201,7 +204,7 @@
processStringArray(
secureSettings.getStringForUser(
ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
- getCurrentUser()),
+ selectedUserInteractor.getSelectedUserId()),
wakeupsConsideredUnlockIntents,
setOf(WAKE_REASON_UNFOLD_DEVICE))
}
@@ -210,7 +213,7 @@
processStringArray(
secureSettings.getStringForUser(
ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
- getCurrentUser()),
+ selectedUserInteractor.getSelectedUserId()),
wakeupsToForceDismissKeyguard,
setOf(WAKE_REASON_UNFOLD_DEVICE))
}
@@ -316,7 +319,8 @@
keyguardUpdateMonitor?.let {
val anyFaceEnrolled = it.isFaceEnrolled
val anyFingerprintEnrolled =
- it.getCachedIsUnlockWithFingerprintPossible(getCurrentUser())
+ it.getCachedIsUnlockWithFingerprintPossible(
+ selectedUserInteractor.getSelectedUserId())
val udfpsEnrolled = it.isUdfpsEnrolled
if (!anyFaceEnrolled && !anyFingerprintEnrolled) {
@@ -371,7 +375,8 @@
"${shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment()}")
pw.println(" faceEnrolled=${it.isFaceEnrolled}")
pw.println(" fpEnrolled=${
- it.getCachedIsUnlockWithFingerprintPossible(getCurrentUser())}")
+ it.getCachedIsUnlockWithFingerprintPossible(
+ selectedUserInteractor.getSelectedUserId())}")
pw.println(" udfpsEnrolled=${it.isUdfpsEnrolled}")
} ?: pw.println(" keyguardUpdateMonitor is uninitialized")
}
diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
index 207f344..58bbdeb 100644
--- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
@@ -45,6 +45,7 @@
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.keyguard.dagger.KeyguardBouncerScope;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import java.util.NoSuchElementException;
@@ -63,6 +64,7 @@
private Handler mHandler;
private IKeyguardClient mClient;
private KeyguardSecurityCallback mKeyguardCallback;
+ private SelectedUserInteractor mSelectedUserInteractor;
private final ServiceConnection mConnection = new ServiceConnection() {
@Override
@@ -76,7 +78,7 @@
} catch (RemoteException e) {
// Failed to link to death, just dismiss and unbind the service for now.
Log.e(TAG, "Lost connection to secondary lockscreen service", e);
- dismiss(KeyguardUpdateMonitor.getCurrentUser());
+ dismiss(mSelectedUserInteractor.getSelectedUserId());
}
}
}
@@ -110,7 +112,7 @@
mView.setChildSurfacePackage(surfacePackage);
} else {
mHandler.post(() -> {
- dismiss(KeyguardUpdateMonitor.getCurrentUser());
+ dismiss(mSelectedUserInteractor.getSelectedUserId());
});
}
}
@@ -131,7 +133,7 @@
protected SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
- final int userId = KeyguardUpdateMonitor.getCurrentUser();
+ final int userId = mSelectedUserInteractor.getSelectedUserId();
mUpdateMonitor.registerCallback(mUpdateCallback);
if (mClient != null) {
@@ -158,7 +160,7 @@
private AdminSecondaryLockScreenController(Context context, KeyguardSecurityContainer parent,
KeyguardUpdateMonitor updateMonitor, KeyguardSecurityCallback callback,
- @Main Handler handler) {
+ @Main Handler handler, SelectedUserInteractor selectedUserInteractor) {
mContext = context;
mHandler = handler;
mParent = parent;
@@ -166,6 +168,7 @@
mKeyguardCallback = callback;
mView = new AdminSecurityView(mContext, mSurfaceHolderCallback);
mView.setId(View.generateViewId());
+ mSelectedUserInteractor = selectedUserInteractor;
}
/**
@@ -218,13 +221,13 @@
}
} catch (RemoteException e) {
Log.e(TAG, "Error in onCreateKeyguardSurface", e);
- dismiss(KeyguardUpdateMonitor.getCurrentUser());
+ dismiss(mSelectedUserInteractor.getSelectedUserId());
}
}
private void dismiss(int userId) {
mHandler.removeCallbacksAndMessages(null);
- if (mView.isAttachedToWindow() && userId == KeyguardUpdateMonitor.getCurrentUser()) {
+ if (mView.isAttachedToWindow() && userId == mSelectedUserInteractor.getSelectedUserId()) {
hide();
if (mKeyguardCallback != null) {
mKeyguardCallback.dismiss(/* securityVerified= */ true, userId,
@@ -265,19 +268,24 @@
private final KeyguardSecurityContainer mParent;
private final KeyguardUpdateMonitor mUpdateMonitor;
private final Handler mHandler;
+ private final SelectedUserInteractor mSelectedUserInteractor;
@Inject
- public Factory(Context context, KeyguardSecurityContainer parent,
- KeyguardUpdateMonitor updateMonitor, @Main Handler handler) {
+ public Factory(Context context,
+ KeyguardSecurityContainer parent,
+ KeyguardUpdateMonitor updateMonitor,
+ @Main Handler handler,
+ SelectedUserInteractor selectedUserInteractor) {
mContext = context;
mParent = parent;
mUpdateMonitor = updateMonitor;
mHandler = handler;
+ mSelectedUserInteractor = selectedUserInteractor;
}
public AdminSecondaryLockScreenController create(KeyguardSecurityCallback callback) {
return new AdminSecondaryLockScreenController(mContext, mParent, mUpdateMonitor,
- callback, mHandler);
+ callback, mHandler, mSelectedUserInteractor);
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
index f7e8eb4..5de370f 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
@@ -43,6 +43,7 @@
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.EmergencyDialerConstants;
import com.android.systemui.util.ViewController;
@@ -67,6 +68,7 @@
private LockPatternUtils mLockPatternUtils;
private Executor mMainExecutor;
private Executor mBackgroundExecutor;
+ private SelectedUserInteractor mSelectedUserInteractor;
private final KeyguardUpdateMonitorCallback mInfoCallback =
new KeyguardUpdateMonitorCallback() {
@@ -96,7 +98,8 @@
ShadeController shadeController,
@Nullable TelecomManager telecomManager, MetricsLogger metricsLogger,
LockPatternUtils lockPatternUtils,
- Executor mainExecutor, Executor backgroundExecutor) {
+ Executor mainExecutor, Executor backgroundExecutor,
+ SelectedUserInteractor selectedUserInteractor) {
super(view);
mConfigurationController = configurationController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -109,6 +112,7 @@
mLockPatternUtils = lockPatternUtils;
mMainExecutor = mainExecutor;
mBackgroundExecutor = backgroundExecutor;
+ mSelectedUserInteractor = selectedUserInteractor;
}
@Override
@@ -142,7 +146,7 @@
mBackgroundExecutor.execute(() -> {
boolean isInCall = mTelecomManager != null && mTelecomManager.isInCall();
boolean isSecure = mLockPatternUtils
- .isSecure(KeyguardUpdateMonitor.getCurrentUser());
+ .isSecure(mSelectedUserInteractor.getSelectedUserId());
mMainExecutor.execute(() -> mView.updateEmergencyCallButton(
/* isInCall= */ isInCall,
/* hasTelephonyRadio= */ getContext().getPackageManager()
@@ -192,7 +196,7 @@
getContext().startActivityAsUser(emergencyDialIntent,
ActivityOptions.makeCustomAnimation(getContext(), 0, 0).toBundle(),
- new UserHandle(KeyguardUpdateMonitor.getCurrentUser()));
+ new UserHandle(mSelectedUserInteractor.getSelectedUserId()));
}
});
});
@@ -218,6 +222,7 @@
private final LockPatternUtils mLockPatternUtils;
private final Executor mMainExecutor;
private final Executor mBackgroundExecutor;
+ private final SelectedUserInteractor mSelectedUserInteractor;
@Inject
public Factory(ConfigurationController configurationController,
@@ -227,7 +232,8 @@
@Nullable TelecomManager telecomManager, MetricsLogger metricsLogger,
LockPatternUtils lockPatternUtils,
@Main Executor mainExecutor,
- @Background Executor backgroundExecutor) {
+ @Background Executor backgroundExecutor,
+ SelectedUserInteractor selectedUserInteractor) {
mConfigurationController = configurationController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -240,6 +246,7 @@
mLockPatternUtils = lockPatternUtils;
mMainExecutor = mainExecutor;
mBackgroundExecutor = backgroundExecutor;
+ mSelectedUserInteractor = selectedUserInteractor;
}
/** Construct an {@link com.android.keyguard.EmergencyButtonController}. */
@@ -247,7 +254,7 @@
return new EmergencyButtonController(view, mConfigurationController,
mKeyguardUpdateMonitor, mTelephonyManager, mPowerManager, mActivityTaskManager,
mShadeController, mTelecomManager, mMetricsLogger, mLockPatternUtils,
- mMainExecutor, mBackgroundExecutor);
+ mMainExecutor, mBackgroundExecutor, mSelectedUserInteractor);
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 167bd59..dad4400 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -34,10 +34,11 @@
import com.android.keyguard.EmergencyButtonController.EmergencyButtonCallback;
import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingClassifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.res.R;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import java.util.HashMap;
import java.util.Map;
@@ -80,9 +81,9 @@
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker, FalsingCollector falsingCollector,
EmergencyButtonController emergencyButtonController,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor) {
super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
- messageAreaControllerFactory, featureFlags);
+ messageAreaControllerFactory, featureFlags, selectedUserInteractor);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
@@ -104,7 +105,7 @@
mEmergencyButtonController.setEmergencyButtonCallback(mEmergencyButtonCallback);
// if the user is currently locked out, enforce it.
long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
- KeyguardUpdateMonitor.getCurrentUser());
+ mSelectedUserInteractor.getSelectedUserId());
if (shouldLockout(deadline)) {
handleAttemptLockout(deadline);
}
@@ -175,7 +176,7 @@
}
void onPasswordChecked(int userId, boolean matched, int timeoutMs, boolean isValidPassword) {
- boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
+ boolean dismissKeyguard = mSelectedUserInteractor.getSelectedUserId() == userId;
if (matched) {
getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0);
if (dismissKeyguard) {
@@ -212,7 +213,7 @@
mPendingLockCheck.cancel(false);
}
- final int userId = KeyguardUpdateMonitor.getCurrentUser();
+ final int userId = mSelectedUserInteractor.getSelectedUserId();
if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
// to avoid accidental lockout, only count attempts that are long enough to be a
// real password. This may require some tweaking.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
index e6a2bfa..d26caa9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
@@ -29,6 +29,7 @@
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.SessionTracker
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import java.io.PrintWriter
import javax.inject.Inject
@@ -42,7 +43,8 @@
class KeyguardBiometricLockoutLogger @Inject constructor(
private val uiEventLogger: UiEventLogger,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val sessionTracker: SessionTracker
+ private val sessionTracker: SessionTracker,
+ private val selectedUserInteractor: SelectedUserInteractor
) : CoreStartable {
private var fingerprintLockedOut = false
private var faceLockedOut = false
@@ -52,7 +54,7 @@
override fun start() {
mKeyguardUpdateMonitorCallback.onStrongAuthStateChanged(
- KeyguardUpdateMonitor.getCurrentUser())
+ selectedUserInteractor.getSelectedUserId())
keyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback)
}
@@ -79,7 +81,7 @@
}
override fun onStrongAuthStateChanged(userId: Int) {
- if (userId != KeyguardUpdateMonitor.getCurrentUser()) {
+ if (userId != selectedUserInteractor.getSelectedUserId()) {
return
}
val strongAuthFlags = keyguardUpdateMonitor.strongAuthTracker
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 29ce18c..b309483 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -29,7 +29,6 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.res.R;
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
import com.android.systemui.bouncer.ui.BouncerMessageView;
import com.android.systemui.bouncer.ui.binder.BouncerMessageViewBinder;
@@ -38,7 +37,9 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.log.BouncerLogger;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -51,7 +52,6 @@
private final SecurityMode mSecurityMode;
private final KeyguardSecurityCallback mKeyguardSecurityCallback;
- private final EmergencyButton mEmergencyButton;
private final EmergencyButtonController mEmergencyButtonController;
private boolean mPaused;
protected KeyguardMessageAreaController<BouncerKeyguardMessageArea> mMessageAreaController;
@@ -61,18 +61,20 @@
// state for the current security method.
private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {};
private final FeatureFlags mFeatureFlags;
+ protected final SelectedUserInteractor mSelectedUserInteractor;
protected KeyguardInputViewController(T view, SecurityMode securityMode,
KeyguardSecurityCallback keyguardSecurityCallback,
EmergencyButtonController emergencyButtonController,
@Nullable KeyguardMessageAreaController.Factory messageAreaControllerFactory,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ SelectedUserInteractor selectedUserInteractor) {
super(view);
mSecurityMode = securityMode;
mKeyguardSecurityCallback = keyguardSecurityCallback;
- mEmergencyButton = view == null ? null : view.findViewById(R.id.emergency_call_button);
mEmergencyButtonController = emergencyButtonController;
mFeatureFlags = featureFlags;
+ mSelectedUserInteractor = selectedUserInteractor;
if (messageAreaControllerFactory != null) {
try {
BouncerKeyguardMessageArea kma = view.requireViewById(R.id.bouncer_message_area);
@@ -207,6 +209,7 @@
private final DevicePostureController mDevicePostureController;
private final KeyguardViewController mKeyguardViewController;
private final FeatureFlags mFeatureFlags;
+ private final SelectedUserInteractor mSelectedUserInteractor;
@Inject
public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -219,7 +222,7 @@
EmergencyButtonController.Factory emergencyButtonControllerFactory,
DevicePostureController devicePostureController,
KeyguardViewController keyguardViewController,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor) {
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
@@ -234,6 +237,7 @@
mDevicePostureController = devicePostureController;
mKeyguardViewController = keyguardViewController;
mFeatureFlags = featureFlags;
+ mSelectedUserInteractor = selectedUserInteractor;
}
/** Create a new {@link KeyguardInputViewController}. */
@@ -248,32 +252,32 @@
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mLatencyTracker, mFalsingCollector,
emergencyButtonController, mMessageAreaControllerFactory,
- mDevicePostureController, mFeatureFlags);
+ mDevicePostureController, mFeatureFlags, mSelectedUserInteractor);
} else if (keyguardInputView instanceof KeyguardPasswordView) {
return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mInputMethodManager, emergencyButtonController, mMainExecutor, mResources,
mFalsingCollector, mKeyguardViewController,
- mDevicePostureController, mFeatureFlags);
+ mDevicePostureController, mFeatureFlags, mSelectedUserInteractor);
} else if (keyguardInputView instanceof KeyguardPINView) {
return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, emergencyButtonController, mFalsingCollector,
- mDevicePostureController, mFeatureFlags);
+ mDevicePostureController, mFeatureFlags, mSelectedUserInteractor);
} else if (keyguardInputView instanceof KeyguardSimPinView) {
return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
- emergencyButtonController, mFeatureFlags);
+ emergencyButtonController, mFeatureFlags, mSelectedUserInteractor);
} else if (keyguardInputView instanceof KeyguardSimPukView) {
return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
- emergencyButtonController, mFeatureFlags);
+ emergencyButtonController, mFeatureFlags, mSelectedUserInteractor);
}
throw new RuntimeException("Unable to find controller for " + keyguardInputView);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 959cf6f..2e21255 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -39,11 +39,12 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.List;
@@ -51,7 +52,6 @@
public class KeyguardPasswordViewController
extends KeyguardAbsKeyInputViewController<KeyguardPasswordView> {
- private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms
private final KeyguardSecurityCallback mKeyguardSecurityCallback;
private final DevicePostureController mPostureController;
private final DevicePostureController.Callback mPostureCallback = posture ->
@@ -112,10 +112,11 @@
FalsingCollector falsingCollector,
KeyguardViewController keyguardViewController,
DevicePostureController postureController,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ SelectedUserInteractor selectedUserInteractor) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, falsingCollector,
- emergencyButtonController, featureFlags);
+ emergencyButtonController, featureFlags, selectedUserInteractor);
mKeyguardSecurityCallback = keyguardSecurityCallback;
mInputMethodManager = inputMethodManager;
mPostureController = postureController;
@@ -132,7 +133,8 @@
@Override
protected void onViewAttached() {
super.onViewAttached();
- mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
+ mPasswordEntry.setTextOperationUser(
+ UserHandle.of(mSelectedUserInteractor.getSelectedUserId()));
mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_VARIATION_PASSWORD);
@@ -164,13 +166,6 @@
// If there's more than one IME, enable the IME switcher button
updateSwitchImeButton();
-
- // When we the current user is switching, InputMethodManagerService sometimes has not
- // switched internal state yet here. As a quick workaround, we check the keyboard state
- // again.
- // TODO: Remove this workaround by ensuring such a race condition never happens.
- mMainExecutor.executeDelayed(
- this::updateSwitchImeButton, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON);
}
@Override
@@ -187,7 +182,8 @@
@Override
void resetState() {
- mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
+ mPasswordEntry.setTextOperationUser(
+ UserHandle.of(mSelectedUserInteractor.getSelectedUserId()));
mMessageAreaController.setMessage(getInitialMessageResId());
final boolean wasDisabled = mPasswordEntry.isEnabled();
mView.setPasswordEntryEnabled(true);
@@ -280,7 +276,7 @@
final boolean shouldIncludeAuxiliarySubtypes) {
final List<InputMethodInfo> enabledImis =
imm.getEnabledInputMethodListAsUser(
- UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
+ UserHandle.of(mSelectedUserInteractor.getSelectedUserId()));
// Number of the filtered IMEs
int filteredImisCount = 0;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 57151ae..db7ff88 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -36,11 +36,12 @@
import com.android.internal.widget.LockscreenCredential;
import com.android.keyguard.EmergencyButtonController.EmergencyButtonCallback;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingClassifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import java.util.HashMap;
import java.util.List;
@@ -110,7 +111,7 @@
mPendingLockCheck.cancel(false);
}
- final int userId = KeyguardUpdateMonitor.getCurrentUser();
+ final int userId = mSelectedUserInteractor.getSelectedUserId();
if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
// Treat single-sized patterns as erroneous taps.
if (pattern.size() == 1) {
@@ -163,7 +164,7 @@
private void onPatternChecked(int userId, boolean matched, int timeoutMs,
boolean isValidPattern) {
- boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
+ boolean dismissKeyguard = mSelectedUserInteractor.getSelectedUserId() == userId;
if (matched) {
getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0);
if (dismissKeyguard) {
@@ -198,9 +199,10 @@
FalsingCollector falsingCollector,
EmergencyButtonController emergencyButtonController,
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
- DevicePostureController postureController, FeatureFlags featureFlags) {
+ DevicePostureController postureController, FeatureFlags featureFlags,
+ SelectedUserInteractor selectedUserInteractor) {
super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
- messageAreaControllerFactory, featureFlags);
+ messageAreaControllerFactory, featureFlags, selectedUserInteractor);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
@@ -223,7 +225,7 @@
mLockPatternView.setOnPatternListener(new UnlockPatternListener());
mLockPatternView.setSaveEnabled(false);
mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
- KeyguardUpdateMonitor.getCurrentUser()));
+ mSelectedUserInteractor.getSelectedUserId()));
mLockPatternView.setOnTouchListener((v, event) -> {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mFalsingCollector.avoidGesture();
@@ -243,7 +245,7 @@
mPostureController.addCallback(mPostureCallback);
// if the user is currently locked out, enforce it.
long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
- KeyguardUpdateMonitor.getCurrentUser());
+ mSelectedUserInteractor.getSelectedUserId());
if (deadline != 0) {
handleAttemptLockout(deadline);
}
@@ -266,7 +268,7 @@
public void reset() {
// reset lock pattern
mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
- KeyguardUpdateMonitor.getCurrentUser()));
+ mSelectedUserInteractor.getSelectedUserId()));
mLockPatternView.enableInput();
mLockPatternView.setEnabled(true);
mLockPatternView.clearPattern();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index aacf866..b7d1171 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -25,9 +25,10 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.res.R;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinBasedInputView>
extends KeyguardAbsKeyInputViewController<T> {
@@ -60,10 +61,11 @@
LiftToActivateListener liftToActivateListener,
EmergencyButtonController emergencyButtonController,
FalsingCollector falsingCollector,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ SelectedUserInteractor selectedUserInteractor) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, falsingCollector,
- emergencyButtonController, featureFlags);
+ emergencyButtonController, featureFlags, selectedUserInteractor);
mLiftToActivateListener = liftToActivateListener;
mFalsingCollector = falsingCollector;
mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
@@ -74,7 +76,7 @@
super.onViewAttached();
boolean showAnimations = !mLockPatternUtils
- .isPinEnhancedPrivacyEnabled(KeyguardUpdateMonitor.getCurrentUser());
+ .isPinEnhancedPrivacyEnabled(mSelectedUserInteractor.getSelectedUserId());
mPasswordEntry.setShowPassword(showAnimations);
for (NumPadKey button : mView.getButtons()) {
button.setOnTouchListener((v, event) -> {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 9a78868..947d90f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -23,11 +23,12 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
public class KeyguardPinViewController
extends KeyguardPinBasedInputViewController<KeyguardPINView> {
@@ -55,17 +56,17 @@
EmergencyButtonController emergencyButtonController,
FalsingCollector falsingCollector,
DevicePostureController postureController,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
- emergencyButtonController, falsingCollector, featureFlags);
+ emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mPostureController = postureController;
mLockPatternUtils = lockPatternUtils;
mFeatureFlags = featureFlags;
view.setIsLockScreenLandscapeEnabled(mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE));
mBackspaceKey = view.findViewById(R.id.delete_button);
- mPinLength = mLockPatternUtils.getPinLength(KeyguardUpdateMonitor.getCurrentUser());
+ mPinLength = mLockPatternUtils.getPinLength(selectedUserInteractor.getSelectedUserId());
}
@Override
@@ -124,7 +125,7 @@
private void updateAutoConfirmationState() {
mDisabledAutoConfirmation = mLockPatternUtils.getCurrentFailedPasswordAttempts(
- KeyguardUpdateMonitor.getCurrentUser()) >= MIN_FAILED_PIN_ATTEMPTS;
+ mSelectedUserInteractor.getSelectedUserId()) >= MIN_FAILED_PIN_ATTEMPTS;
updateOKButtonVisibility();
updateBackSpaceVisibility();
updatePinHinting();
@@ -179,7 +180,8 @@
*/
private boolean isAutoPinConfirmEnabledInSettings() {
//Checks if user has enabled the auto confirm in Settings
- return mLockPatternUtils.isAutoPinConfirmEnabled(KeyguardUpdateMonitor.getCurrentUser())
+ return mLockPatternUtils.isAutoPinConfirmEnabled(
+ mSelectedUserInteractor.getSelectedUserId())
&& mPinLength != LockPatternUtils.PIN_LENGTH_UNAVAILABLE;
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 51dafac..7101ed5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -91,7 +91,7 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.user.domain.interactor.UserInteractor;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.GlobalSettings;
@@ -157,23 +157,25 @@
private int mCurrentUser = UserHandle.USER_NULL;
private UserSwitcherController.UserSwitchCallback mUserSwitchCallback =
new UserSwitcherController.UserSwitchCallback() {
- @Override
- public void onUserSwitched() {
- if (mCurrentUser == KeyguardUpdateMonitor.getCurrentUser()) {
- return;
- }
- mCurrentUser = KeyguardUpdateMonitor.getCurrentUser();
- showPrimarySecurityScreen(false);
- if (mCurrentSecurityMode != SecurityMode.SimPin
- && mCurrentSecurityMode != SecurityMode.SimPuk) {
- reinflateViewFlipper((l) -> {});
- }
- }
- };
+ @Override
+ public void onUserSwitched() {
+ if (mCurrentUser == mSelectedUserInteractor.getSelectedUserId()) {
+ return;
+ }
+ mCurrentUser = mSelectedUserInteractor.getSelectedUserId();
+ showPrimarySecurityScreen(false);
+ if (mCurrentSecurityMode != SecurityMode.SimPin
+ && mCurrentSecurityMode != SecurityMode.SimPuk) {
+ reinflateViewFlipper((l) -> {
+ });
+ }
+ }
+ };
@VisibleForTesting
final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() {
private MotionEvent mTouchDown;
+
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
@@ -267,7 +269,8 @@
ThreadUtils.postOnBackgroundThread(() -> {
try {
Thread.sleep(5000);
- } catch (InterruptedException ignored) { }
+ } catch (InterruptedException ignored) {
+ }
System.gc();
System.runFinalization();
System.gc();
@@ -281,7 +284,7 @@
mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
.setType(success ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_FAILURE));
mUiEventLogger.log(success ? BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS
- : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE, getSessionId());
+ : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE, getSessionId());
}
@Override
@@ -404,7 +407,7 @@
}
mKeyguardSecurityCallback.dismiss(
false /* authenticated */,
- KeyguardUpdateMonitor.getCurrentUser(),
+ mSelectedUserInteractor.getSelectedUserId(),
/* bypassSecondaryLockScreen */ false,
SecurityMode.Invalid
);
@@ -420,12 +423,13 @@
showPrimarySecurityScreen(false);
}
};
- private final UserInteractor mUserInteractor;
+ private final SelectedUserInteractor mSelectedUserInteractor;
private final Provider<DeviceEntryInteractor> mDeviceEntryInteractor;
private final Provider<JavaAdapter> mJavaAdapter;
private final DeviceProvisionedController mDeviceProvisionedController;
private final Lazy<PrimaryBouncerInteractor> mPrimaryBouncerInteractor;
- @Nullable private Job mSceneTransitionCollectionJob;
+ @Nullable
+ private Job mSceneTransitionCollectionJob;
@Inject
public KeyguardSecurityContainerController(KeyguardSecurityContainer view,
@@ -453,7 +457,7 @@
KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
BouncerMessageInteractor bouncerMessageInteractor,
Provider<JavaAdapter> javaAdapter,
- UserInteractor userInteractor,
+ SelectedUserInteractor selectedUserInteractor,
DeviceProvisionedController deviceProvisionedController,
FaceAuthAccessibilityDelegate faceAuthAccessibilityDelegate,
KeyguardTransitionInteractor keyguardTransitionInteractor,
@@ -487,7 +491,7 @@
mAudioManager = audioManager;
mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
mBouncerMessageInteractor = bouncerMessageInteractor;
- mUserInteractor = userInteractor;
+ mSelectedUserInteractor = selectedUserInteractor;
mDeviceEntryInteractor = deviceEntryInteractor;
mJavaAdapter = javaAdapter;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
@@ -520,10 +524,10 @@
// When the scene framework says that the lockscreen has been dismissed, dismiss the
// keyguard here, revealing the underlying app or launcher:
mSceneTransitionCollectionJob = mJavaAdapter.get().alwaysCollectFlow(
- mDeviceEntryInteractor.get().isDeviceEntered(),
+ mDeviceEntryInteractor.get().isDeviceEntered(),
isDeviceEntered -> {
if (isDeviceEntered) {
- final int selectedUserId = mUserInteractor.getSelectedUserId();
+ final int selectedUserId = mSelectedUserInteractor.getSelectedUserId();
showNextSecurityScreenOrFinish(
/* authenticated= */ true,
selectedUserId,
@@ -548,7 +552,7 @@
}
}
- /** */
+ /** */
public void onPause() {
if (DEBUG) {
Log.d(TAG, String.format("screen off, instance %s at %s",
@@ -586,12 +590,13 @@
/**
* Shows the primary security screen for the user. This will be either the multi-selector
* or the user's security method.
+ *
* @param turningOff true if the device is being turned off
*/
public void showPrimarySecurityScreen(boolean turningOff) {
if (DEBUG) Log.d(TAG, "show()");
SecurityMode securityMode = whitelistIpcs(() -> mSecurityModel.getSecurityMode(
- KeyguardUpdateMonitor.getCurrentUser()));
+ mSelectedUserInteractor.getSelectedUserId()));
if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
showSecurityScreen(securityMode);
}
@@ -671,6 +676,7 @@
/**
* Dismisses the keyguard by going to the next screen or making it gone.
+ *
* @param targetUserId a user that needs to be the foreground user at the dismissal completion.
* @return True if the keyguard is done.
*/
@@ -716,7 +722,7 @@
}
/**
- * Resets the state of the views.
+ * Resets the state of the views.
*/
public void reset() {
mView.reset();
@@ -748,7 +754,7 @@
getCurrentSecurityController(controller -> controller.onResume(reason));
}
mView.onResume(
- mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser()),
+ mSecurityModel.getSecurityMode(mSelectedUserInteractor.getSelectedUserId()),
mKeyguardStateController.isFaceAuthEnabled());
}
@@ -764,7 +770,6 @@
/**
* Show the bouncer and start appear animations.
- *
*/
public void appear() {
// We might still be collapsed and the view didn't have time to layout yet or still
@@ -823,13 +828,16 @@
/**
* Shows the next security screen if there is one.
- * @param authenticated true if the user entered the correct authentication
- * @param targetUserId a user that needs to be the foreground user at the finish (if called)
- * completion.
+ *
+ * @param authenticated true if the user entered the correct authentication
+ * @param targetUserId a user that needs to be the foreground user at the finish
+ * (if called)
+ * completion.
* @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary
- * secondary lock screen requirement, if any.
- * @param expectedSecurityMode SecurityMode that is invoking this request. SecurityMode.Invalid
- * indicates that no check should be done
+ * secondary lock screen requirement, if any.
+ * @param expectedSecurityMode SecurityMode that is invoking this request.
+ * SecurityMode.Invalid
+ * indicates that no check should be done
* @return true if keyguard is done
*/
public boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId,
@@ -879,7 +887,7 @@
// Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
boolean isLockscreenDisabled = mLockPatternUtils.isLockScreenDisabled(
- KeyguardUpdateMonitor.getCurrentUser())
+ mSelectedUserInteractor.getSelectedUserId())
|| !mDeviceProvisionedController.isUserSetup(targetUserId);
if (securityMode == SecurityMode.None && isLockscreenDisabled) {
@@ -955,6 +963,7 @@
* Allows the media keys to work when the keyguard is showing.
* The media keys should be of no interest to the actual keyguard view(s),
* so intercepting them here should not be of any harm.
+ *
* @param event The key event
* @return whether the event was consumed as a media key.
*/
@@ -1050,8 +1059,6 @@
/**
* Switches to the given security view unless it's already being shown, in which case
* this is a no-op.
- *
- * @param securityMode
*/
@VisibleForTesting
void showSecurityScreen(SecurityMode securityMode) {
@@ -1230,6 +1237,7 @@
* Fades and translates in/out the security screen.
* Fades in as expansion approaches 0.
* Animation duration is between 0.33f and 0.67f of panel expansion fraction.
+ *
* @param fraction amount of the screen that should show.
*/
public void setExpansion(float fraction) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index d2d0517..6e24208 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -43,6 +43,7 @@
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.res.R;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
public class KeyguardSimPinViewController
extends KeyguardPinBasedInputViewController<KeyguardSimPinView> {
@@ -83,10 +84,11 @@
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
TelephonyManager telephonyManager, FalsingCollector falsingCollector,
- EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags) {
+ EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags,
+ SelectedUserInteractor selectedUserInteractor) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
- emergencyButtonController, falsingCollector, featureFlags);
+ emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mTelephonyManager = telephonyManager;
mSimImageView = mView.findViewById(R.id.keyguard_sim);
@@ -168,7 +170,7 @@
mRemainingAttempts = -1;
mShowDefaultMessage = true;
getKeyguardSecurityCallback().dismiss(
- true, KeyguardUpdateMonitor.getCurrentUser(),
+ true, mSelectedUserInteractor.getSelectedUserId(),
SecurityMode.SimPin);
} else {
mShowDefaultMessage = false;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index b52a36b..13f9d3e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -40,6 +40,7 @@
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.res.R;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
public class KeyguardSimPukViewController
extends KeyguardPinBasedInputViewController<KeyguardSimPukView> {
@@ -70,7 +71,8 @@
if (simState == TelephonyManager.SIM_STATE_READY) {
mRemainingAttempts = -1;
mShowDefaultMessage = true;
- getKeyguardSecurityCallback().dismiss(true, KeyguardUpdateMonitor.getCurrentUser(),
+ getKeyguardSecurityCallback().dismiss(
+ true, mSelectedUserInteractor.getSelectedUserId(),
SecurityMode.SimPuk);
} else {
resetState();
@@ -87,10 +89,11 @@
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
TelephonyManager telephonyManager, FalsingCollector falsingCollector,
- EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags) {
+ EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags,
+ SelectedUserInteractor selectedUserInteractor) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
- emergencyButtonController, falsingCollector, featureFlags);
+ emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mTelephonyManager = telephonyManager;
mSimImageView = mView.findViewById(R.id.keyguard_sim);
@@ -284,7 +287,7 @@
mShowDefaultMessage = true;
getKeyguardSecurityCallback().dismiss(
- true, KeyguardUpdateMonitor.getCurrentUser(),
+ true, mSelectedUserInteractor.getSelectedUserId(),
SecurityMode.SimPuk);
} else {
mShowDefaultMessage = false;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 205c297..7d6240b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -186,8 +186,8 @@
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.DevicePostureController.DevicePostureInt;
import com.android.systemui.telephony.TelephonyListenerManager;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.Assert;
-import com.android.systemui.util.settings.SecureSettings;
import dalvik.annotation.optimization.NeverCompile;
@@ -410,7 +410,6 @@
private final DevicePolicyManager mDevicePolicyManager;
private final DevicePostureController mPostureController;
private final BroadcastDispatcher mBroadcastDispatcher;
- private final SecureSettings mSecureSettings;
private final InteractionJankMonitor mInteractionJankMonitor;
private final LatencyTracker mLatencyTracker;
private final StatusBarStateController mStatusBarStateController;
@@ -429,6 +428,7 @@
private final IActivityTaskManager mActivityTaskManager;
private final WakefulnessLifecycle mWakefulness;
private final DisplayTracker mDisplayTracker;
+ private final SelectedUserInteractor mSelectedUserInteractor;
private final LockPatternUtils mLockPatternUtils;
@VisibleForTesting
@DevicePostureInt
@@ -537,13 +537,14 @@
private static int sCurrentUser;
+ @Deprecated
public synchronized static void setCurrentUser(int currentUser) {
sCurrentUser = currentUser;
}
/**
* @deprecated This can potentially return unexpected values in a multi user scenario
- * as this state is managed by another component. Consider using {@link UserTracker}.
+ * as this state is managed by another component. Consider using {@link SelectedUserInteractor}.
*/
@Deprecated
public synchronized static int getCurrentUser() {
@@ -577,7 +578,7 @@
if (enabled) {
String message = null;
- if (KeyguardUpdateMonitor.getCurrentUser() == userId
+ if (mSelectedUserInteractor.getSelectedUserId() == userId
&& trustGrantedMessages != null) {
// Show the first non-empty string provided by a trust agent OR intentionally pass
// an empty string through (to prevent the default trust agent string from showing)
@@ -590,7 +591,7 @@
}
mLogger.logTrustGrantedWithFlags(flags, newlyUnlocked, userId, message);
- if (userId == getCurrentUser()) {
+ if (userId == mSelectedUserInteractor.getSelectedUserId()) {
if (newlyUnlocked) {
// if this callback is ever removed, this should then be logged in
// TrustRepository
@@ -1038,7 +1039,7 @@
mHandler.removeCallbacks(mFpCancelNotReceived);
}
try {
- final int userId = mUserTracker.getUserId();
+ final int userId = mSelectedUserInteractor.getSelectedUserId(true);
if (userId != authUserId) {
mLogger.logFingerprintAuthForWrongUser(authUserId);
return;
@@ -1127,8 +1128,8 @@
lockedOutStateChanged = !mFingerprintLockedOutPermanent;
mFingerprintLockedOutPermanent = true;
mLogger.d("Fingerprint permanently locked out - requiring stronger auth");
- mLockPatternUtils.requireStrongAuth(
- STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, getCurrentUser());
+ mLockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
+ mSelectedUserInteractor.getSelectedUserId());
}
if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
@@ -1309,7 +1310,7 @@
mLogger.d("Aborted successful auth because device is going to sleep.");
return;
}
- final int userId = mUserTracker.getUserId();
+ final int userId = mSelectedUserInteractor.getSelectedUserId(true);
if (userId != authUserId) {
mLogger.logFaceAuthForWrongUser(authUserId);
return;
@@ -1565,7 +1566,8 @@
@Deprecated
public boolean getIsFaceAuthenticated() {
boolean faceAuthenticated = false;
- BiometricAuthenticated bioFaceAuthenticated = mUserFaceAuthenticated.get(getCurrentUser());
+ BiometricAuthenticated bioFaceAuthenticated =
+ mUserFaceAuthenticated.get(mSelectedUserInteractor.getSelectedUserId());
if (bioFaceAuthenticated != null) {
faceAuthenticated = bioFaceAuthenticated.mAuthenticated;
}
@@ -1754,9 +1756,10 @@
cb.onStrongAuthStateChanged(userId);
}
}
- if (userId == getCurrentUser()) {
+ if (userId == mSelectedUserInteractor.getSelectedUserId()) {
FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED.setExtraInfo(
- mStrongAuthTracker.getStrongAuthForUser(getCurrentUser()));
+ mStrongAuthTracker.getStrongAuthForUser(
+ mSelectedUserInteractor.getSelectedUserId()));
// Strong auth is only reset when primary auth is used to enter the device,
// so we only check whether to stop biometric listening states here
@@ -1783,10 +1786,10 @@
cb.onNonStrongBiometricAllowedChanged(userId);
}
}
- if (userId == getCurrentUser()) {
+ if (userId == mSelectedUserInteractor.getSelectedUserId()) {
FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED.setExtraInfo(
mStrongAuthTracker.isNonStrongBiometricAllowedAfterIdleTimeout(
- getCurrentUser()) ? -1 : 1);
+ mSelectedUserInteractor.getSelectedUserId()) ? -1 : 1);
// This is only reset when primary auth is used to enter the device, so we only check
// whether to stop biometric listening states here
@@ -2191,12 +2194,12 @@
}
public boolean isUnlockingWithBiometricAllowed(boolean isStrongBiometric) {
- int userId = getCurrentUser();
+ int userId = mSelectedUserInteractor.getSelectedUserId();
return isBiometricAllowedForUser(isStrongBiometric, userId);
}
public boolean hasUserAuthenticatedSinceBoot() {
- int userId = getCurrentUser();
+ int userId = mSelectedUserInteractor.getSelectedUserId();
return (getStrongAuthForUser(userId)
& STRONG_AUTH_REQUIRED_AFTER_BOOT) == 0;
}
@@ -2344,7 +2347,6 @@
UserTracker userTracker,
@Main Looper mainLooper,
BroadcastDispatcher broadcastDispatcher,
- SecureSettings secureSettings,
DumpManager dumpManager,
@Background Executor backgroundExecutor,
@Main Executor mainExecutor,
@@ -2376,7 +2378,8 @@
TaskStackChangeListeners taskStackChangeListeners,
IActivityTaskManager activityTaskManagerService,
DisplayTracker displayTracker,
- WakefulnessLifecycle wakefulness) {
+ WakefulnessLifecycle wakefulness,
+ SelectedUserInteractor selectedUserInteractor) {
mContext = context;
mSubscriptionManager = subscriptionManager;
mUserTracker = userTracker;
@@ -2392,7 +2395,6 @@
mStatusBarState = mStatusBarStateController.getState();
mLockPatternUtils = lockPatternUtils;
mAuthController = authController;
- mSecureSettings = secureSettings;
dumpManager.registerDumpable(this);
mSensorPrivacyManager = sensorPrivacyManager;
mActiveUnlockConfig = activeUnlockConfiguration;
@@ -2426,6 +2428,7 @@
mWakefulness = wakefulness;
mDisplayTracker = displayTracker;
mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor);
+ mSelectedUserInteractor = selectedUserInteractor;
mHandler = new Handler(mainLooper) {
@Override
@@ -2648,7 +2651,7 @@
mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener);
mIsSystemUser = mUserManager.isSystemUser();
- int user = mUserTracker.getUserId();
+ int user = mSelectedUserInteractor.getSelectedUserId(true);
mUserIsUnlocked.put(user, mUserManager.isUserUnlocked(user));
mLogoutEnabled = mDevicePolicyManager.isLogoutEnabled();
updateSecondaryLockscreenRequirement(user);
@@ -2720,7 +2723,7 @@
* @return true if there's at least one udfps enrolled for the current user.
*/
public boolean isUdfpsEnrolled() {
- return mAuthController.isUdfpsEnrolled(getCurrentUser());
+ return mAuthController.isUdfpsEnrolled(mSelectedUserInteractor.getSelectedUserId());
}
/**
@@ -2735,7 +2738,7 @@
* @return true if there's at least one sfps enrollment for the current user.
*/
public boolean isSfpsEnrolled() {
- return mAuthController.isSfpsEnrolled(getCurrentUser());
+ return mAuthController.isSfpsEnrolled(mSelectedUserInteractor.getSelectedUserId());
}
/**
@@ -2906,7 +2909,7 @@
if (shouldTriggerActiveUnlock()) {
mLogger.logActiveUnlockTriggered(reason);
- mTrustManager.reportUserMayRequestUnlock(KeyguardUpdateMonitor.getCurrentUser());
+ mTrustManager.reportUserMayRequestUnlock(mSelectedUserInteractor.getSelectedUserId());
}
}
@@ -2960,7 +2963,7 @@
if (allowRequest && shouldTriggerActiveUnlock()) {
mLogger.logUserRequestedUnlock(requestOrigin, reason, dismissKeyguard);
- mTrustManager.reportUserRequestedUnlock(KeyguardUpdateMonitor.getCurrentUser(),
+ mTrustManager.reportUserRequestedUnlock(mSelectedUserInteractor.getSelectedUserId(),
dismissKeyguard);
}
}
@@ -3031,7 +3034,7 @@
&& mStatusBarState != StatusBarState.SHADE_LOCKED);
// Gates:
- final int user = getCurrentUser();
+ final int user = mSelectedUserInteractor.getSelectedUserId();
// No need to trigger active unlock if we're already unlocked or don't have
// pin/pattern/password setup
@@ -3073,30 +3076,33 @@
}
private boolean shouldListenForFingerprintAssistant() {
- BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(getCurrentUser());
+ BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(
+ mSelectedUserInteractor.getSelectedUserId());
return mAssistantVisible && mKeyguardOccluded
&& !(fingerprint != null && fingerprint.mAuthenticated)
- && !mUserHasTrust.get(getCurrentUser(), false);
+ && !mUserHasTrust.get(
+ mSelectedUserInteractor.getSelectedUserId(), false);
}
private boolean shouldListenForFaceAssistant() {
- BiometricAuthenticated face = mUserFaceAuthenticated.get(getCurrentUser());
+ BiometricAuthenticated face = mUserFaceAuthenticated.get(
+ mSelectedUserInteractor.getSelectedUserId());
return mAssistantVisible
// There can be intermediate states where mKeyguardShowing is false but
// mKeyguardOccluded is true, we don't want to run face auth in such a scenario.
&& (mKeyguardShowing && mKeyguardOccluded)
&& !(face != null && face.mAuthenticated)
- && !mUserHasTrust.get(getCurrentUser(), false);
+ && !mUserHasTrust.get(mSelectedUserInteractor.getSelectedUserId(), false);
}
private boolean shouldTriggerActiveUnlockForAssistant() {
return mAssistantVisible && mKeyguardOccluded
- && !mUserHasTrust.get(getCurrentUser(), false);
+ && !mUserHasTrust.get(mSelectedUserInteractor.getSelectedUserId(), false);
}
@VisibleForTesting
protected boolean shouldListenForFingerprint(boolean isUdfps) {
- final int user = getCurrentUser();
+ final int user = mSelectedUserInteractor.getSelectedUserId();
final boolean userDoesNotHaveTrust = !getUserHasTrust(user);
final boolean shouldListenForFingerprintAssistant = shouldListenForFingerprintAssistant();
final boolean shouldListenKeyguardState =
@@ -3185,7 +3191,7 @@
final boolean statusBarShadeLocked = mStatusBarState == StatusBarState.SHADE_LOCKED;
final boolean awakeKeyguard = isKeyguardVisible() && mDeviceInteractive
&& !statusBarShadeLocked;
- final int user = getCurrentUser();
+ final int user = mSelectedUserInteractor.getSelectedUserId();
final boolean faceAuthAllowed = isUnlockingWithBiometricAllowed(FACE);
final boolean canBypass = mKeyguardBypassController != null
&& mKeyguardBypassController.canBypass();
@@ -3285,7 +3291,7 @@
}
private void startListeningForFingerprint() {
- final int userId = getCurrentUser();
+ final int userId = mSelectedUserInteractor.getSelectedUserId();
final boolean unlockPossible = isUnlockWithFingerprintPossible(userId);
if (mFingerprintCancelSignal != null) {
mLogger.logUnexpectedFpCancellationSignalState(
@@ -3332,7 +3338,7 @@
}
private void startListeningForFace(@NonNull FaceAuthUiEvent faceAuthUiEvent) {
- final int userId = getCurrentUser();
+ final int userId = mSelectedUserInteractor.getSelectedUserId();
final boolean unlockPossible = isUnlockWithFacePossible(userId);
if (mFaceCancelSignal != null) {
mLogger.logUnexpectedFaceCancellationSignalState(mFaceRunningState, unlockPossible);
@@ -3875,12 +3881,12 @@
}
private boolean resolveNeedsSlowUnlockTransition() {
- if (isUserUnlocked(getCurrentUser())) {
+ if (isUserUnlocked(mSelectedUserInteractor.getSelectedUserId())) {
return false;
}
Intent homeIntent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME);
ResolveInfo resolveInfo = mPackageManager.resolveActivityAsUser(homeIntent,
- 0 /* flags */, getCurrentUser());
+ 0 /* flags */, mSelectedUserInteractor.getSelectedUserId());
if (resolveInfo == null) {
mLogger.w("resolveNeedsSlowUnlockTransition: returning false since activity could "
@@ -4066,9 +4072,11 @@
@AnyThread
public void setSwitchingUser(boolean switching) {
if (switching) {
- mLogger.logUserSwitching(getCurrentUser(), "from setSwitchingUser");
+ mLogger.logUserSwitching(
+ mSelectedUserInteractor.getSelectedUserId(), "from setSwitchingUser");
} else {
- mLogger.logUserSwitchComplete(getCurrentUser(), "from setSwitchingUser");
+ mLogger.logUserSwitchComplete(
+ mSelectedUserInteractor.getSelectedUserId(), "from setSwitchingUser");
}
mSwitchingUser = switching;
// Since this comes in on a binder thread, we need to post it first
@@ -4443,9 +4451,10 @@
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("KeyguardUpdateMonitor state:");
- pw.println(" getUserHasTrust()=" + getUserHasTrust(getCurrentUser()));
+ pw.println(" getUserHasTrust()=" + getUserHasTrust(
+ mSelectedUserInteractor.getSelectedUserId()));
pw.println(" getUserUnlockedWithBiometric()="
- + getUserUnlockedWithBiometric(getCurrentUser()));
+ + getUserUnlockedWithBiometric(mSelectedUserInteractor.getSelectedUserId()));
pw.println(" isFaceAuthInteractorEnabled: " + isFaceAuthInteractorEnabled());
pw.println(" SIM States:");
for (SimData data : mSimDatas.values()) {
@@ -4463,7 +4472,7 @@
pw.println(" " + subId + "=" + mServiceStates.get(subId));
}
if (isFingerprintSupported()) {
- final int userId = mUserTracker.getUserId();
+ final int userId = mSelectedUserInteractor.getSelectedUserId(true);
final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId);
pw.println(" Fingerprint state (user=" + userId + ")");
@@ -4506,7 +4515,7 @@
mFingerprintListenBuffer.toList()
).printTableData(pw);
} else if (mFpm != null && mFingerprintSensorProperties.isEmpty()) {
- final int userId = mUserTracker.getUserId();
+ final int userId = mSelectedUserInteractor.getSelectedUserId(true);
pw.println(" Fingerprint state (user=" + userId + ")");
pw.println(" mFingerprintSensorProperties.isEmpty="
+ mFingerprintSensorProperties.isEmpty());
@@ -4520,7 +4529,7 @@
).printTableData(pw);
}
if (isFaceSupported()) {
- final int userId = mUserTracker.getUserId();
+ final int userId = mSelectedUserInteractor.getSelectedUserId(true);
final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
BiometricAuthenticated face = mUserFaceAuthenticated.get(userId);
pw.println(" Face authentication state (user=" + userId + ")");
@@ -4550,7 +4559,7 @@
mFaceListenBuffer.toList()
).printTableData(pw);
} else if (mFaceManager != null && mFaceSensorProperties.isEmpty()) {
- final int userId = mUserTracker.getUserId();
+ final int userId = mSelectedUserInteractor.getSelectedUserId(true);
pw.println(" Face state (user=" + userId + ")");
pw.println(" mFaceSensorProperties.isEmpty="
+ mFaceSensorProperties.isEmpty());
@@ -4564,7 +4573,7 @@
).printTableData(pw);
}
pw.println("ActiveUnlockRunning="
- + mTrustManager.isActiveUnlockRunning(KeyguardUpdateMonitor.getCurrentUser()));
+ + mTrustManager.isActiveUnlockRunning(mSelectedUserInteractor.getSelectedUserId()));
new DumpsysTableLogger(
"KeyguardActiveUnlockTriggers",
KeyguardActiveUnlockModel.TABLE_HEADERS,
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index 40d0be1..ff6a3d0 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -25,7 +25,6 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
@@ -105,18 +104,6 @@
mLockIcon.setImageTintList(ColorStateList.valueOf(mLockIconColor));
}
- void setImageDrawable(Drawable drawable) {
- mLockIcon.setImageDrawable(drawable);
-
- if (!mUseBackground) return;
-
- if (drawable == null) {
- mBgView.setVisibility(View.INVISIBLE);
- } else {
- mBgView.setVisibility(View.VISIBLE);
- }
- }
-
/**
* Whether or not to render the lock icon background. Mainly used for UDPFS.
*/
@@ -197,6 +184,7 @@
mLockIcon = new ImageView(context, attrs);
mLockIcon.setId(R.id.lock_icon);
mLockIcon.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ mLockIcon.setImageDrawable(context.getDrawable(R.drawable.super_lock_icon));
addView(mLockIcon);
LayoutParams lp = (LayoutParams) mLockIcon.getLayoutParams();
lp.height = MATCH_PARENT;
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 83da80f..611283f 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -35,7 +35,6 @@
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.drawable.AnimatedStateListDrawable;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricSourceType;
import android.os.Process;
@@ -120,9 +119,6 @@
private boolean mUdfpsEnrolled;
private Resources mResources;
private Context mContext;
-
- @NonNull private final AnimatedStateListDrawable mIcon;
-
@NonNull private CharSequence mUnlockedLabel;
@NonNull private CharSequence mLockedLabel;
@NonNull private final VibratorHelper mVibrator;
@@ -147,7 +143,6 @@
private boolean mCanDismissLockScreen;
private int mStatusBarState;
private boolean mIsKeyguardShowing;
- private Runnable mOnGestureDetectedRunnable;
private Runnable mLongPressCancelRunnable;
private boolean mUdfpsSupported;
@@ -232,9 +227,6 @@
mMaxBurnInOffsetX = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
-
- mIcon = (AnimatedStateListDrawable)
- resources.getDrawable(R.drawable.super_lock_icon, context.getTheme());
mUnlockedLabel = resources.getString(R.string.accessibility_unlock_button);
mLockedLabel = resources.getString(R.string.accessibility_lock_icon);
mLongPressTimeout = resources.getInteger(R.integer.config_lockIconLongPress);
@@ -270,7 +262,6 @@
@SuppressLint("ClickableViewAccessibility")
public void setLockIconView(LockIconView lockIconView) {
mView = lockIconView;
- mView.setImageDrawable(mIcon);
mView.setAccessibilityDelegate(mAccessibilityDelegate);
if (mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
@@ -492,10 +483,6 @@
pw.println("mUdfpsSupported: " + mUdfpsSupported);
pw.println("mUdfpsEnrolled: " + mUdfpsEnrolled);
pw.println("mIsKeyguardShowing: " + mIsKeyguardShowing);
- pw.println(" mIcon: ");
- for (int state : mIcon.getState()) {
- pw.print(" " + state);
- }
pw.println();
pw.println(" mShowUnlockIcon: " + mShowUnlockIcon);
pw.println(" mShowLockIcon: " + mShowLockIcon);
diff --git a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
index 2d2ebe9..d33d279 100644
--- a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
@@ -17,6 +17,7 @@
package com.android.systemui
import android.content.Context
+import android.content.res.Resources
import android.graphics.Path
import android.graphics.Rect
import android.graphics.RectF
@@ -33,37 +34,32 @@
*/
class CameraAvailabilityListener(
private val cameraManager: CameraManager,
- private val cutoutProtectionPath: Path,
- private val targetCameraId: String,
+ private val cameraProtectionInfoList: List<CameraProtectionInfo>,
excludedPackages: String,
private val executor: Executor
) {
- private var cutoutBounds = Rect()
private val excludedPackageIds: Set<String>
private val listeners = mutableListOf<CameraTransitionCallback>()
private val availabilityCallback: CameraManager.AvailabilityCallback =
object : CameraManager.AvailabilityCallback() {
override fun onCameraClosed(cameraId: String) {
- if (targetCameraId == cameraId) {
- notifyCameraInactive()
+ cameraProtectionInfoList.forEach {
+ if (cameraId == it.cameraId) {
+ notifyCameraInactive()
+ }
}
}
override fun onCameraOpened(cameraId: String, packageId: String) {
- if (targetCameraId == cameraId && !isExcluded(packageId)) {
- notifyCameraActive()
+ cameraProtectionInfoList.forEach {
+ if (cameraId == it.cameraId && !isExcluded(packageId)) {
+ notifyCameraActive(it)
+ }
}
}
}
init {
- val computed = RectF()
- cutoutProtectionPath.computeBounds(computed, false /* unused */)
- cutoutBounds.set(
- computed.left.roundToInt(),
- computed.top.roundToInt(),
- computed.right.roundToInt(),
- computed.bottom.roundToInt())
excludedPackageIds = excludedPackages.split(",").toSet()
}
@@ -100,8 +96,10 @@
cameraManager.unregisterAvailabilityCallback(availabilityCallback)
}
- private fun notifyCameraActive() {
- listeners.forEach { it.onApplyCameraProtection(cutoutProtectionPath, cutoutBounds) }
+ private fun notifyCameraActive(info: CameraProtectionInfo) {
+ listeners.forEach {
+ it.onApplyCameraProtection(info.cutoutProtectionPath, info.cutoutBounds)
+ }
}
private fun notifyCameraInactive() {
@@ -121,12 +119,11 @@
val manager = context
.getSystemService(Context.CAMERA_SERVICE) as CameraManager
val res = context.resources
- val pathString = res.getString(R.string.config_frontBuiltInDisplayCutoutProtection)
- val cameraId = res.getString(R.string.config_protectedCameraId)
+ val cameraProtectionInfoList = loadCameraProtectionInfoList(res)
val excluded = res.getString(R.string.config_cameraProtectionExcludedPackages)
return CameraAvailabilityListener(
- manager, pathFromString(pathString), cameraId, excluded, executor)
+ manager, cameraProtectionInfoList, excluded, executor)
}
private fun pathFromString(pathString: String): Path {
@@ -140,5 +137,53 @@
return p
}
+
+ private fun loadCameraProtectionInfoList(res: Resources): List<CameraProtectionInfo> {
+ val list = mutableListOf<CameraProtectionInfo>()
+ val front = loadCameraProtectionInfo(
+ res,
+ R.string.config_protectedCameraId,
+ R.string.config_frontBuiltInDisplayCutoutProtection
+ )
+ if (front != null) {
+ list.add(front)
+ }
+ val inner = loadCameraProtectionInfo(
+ res,
+ R.string.config_protectedInnerCameraId,
+ R.string.config_innerBuiltInDisplayCutoutProtection
+ )
+ if (inner != null) {
+ list.add(inner)
+ }
+ return list
+ }
+
+ private fun loadCameraProtectionInfo(
+ res: Resources,
+ cameraIdRes: Int,
+ pathRes: Int
+ ): CameraProtectionInfo? {
+ val cameraId = res.getString(cameraIdRes)
+ if (cameraId == null || cameraId.isEmpty()) {
+ return null
+ }
+ val protectionPath = pathFromString(res.getString(pathRes))
+ val computed = RectF()
+ protectionPath.computeBounds(computed)
+ val protectionBounds = Rect(
+ computed.left.roundToInt(),
+ computed.top.roundToInt(),
+ computed.right.roundToInt(),
+ computed.bottom.roundToInt()
+ )
+ return CameraProtectionInfo(cameraId, protectionPath, protectionBounds)
+ }
}
+
+ data class CameraProtectionInfo (
+ val cameraId: String,
+ val cutoutProtectionPath: Path,
+ val cutoutBounds: Rect
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index b33d501..c860979 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -31,7 +31,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -53,25 +53,25 @@
private static final String
ACTION_FACE_WAKE =
"com.android.systemui.latency.ACTION_FACE_WAKE";
- private final BiometricUnlockController mBiometricUnlockController;
private final BroadcastDispatcher mBroadcastDispatcher;
private final DeviceConfigProxy mDeviceConfigProxy;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final SelectedUserInteractor mSelectedUserInteractor;
private boolean mEnabled;
@Inject
public LatencyTester(
- BiometricUnlockController biometricUnlockController,
BroadcastDispatcher broadcastDispatcher,
DeviceConfigProxy deviceConfigProxy,
@Main DelayableExecutor mainExecutor,
- KeyguardUpdateMonitor keyguardUpdateMonitor
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SelectedUserInteractor selectedUserInteractor
) {
- mBiometricUnlockController = biometricUnlockController;
mBroadcastDispatcher = broadcastDispatcher;
mDeviceConfigProxy = deviceConfigProxy;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mSelectedUserInteractor = selectedUserInteractor;
updateEnabled();
mDeviceConfigProxy.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
@@ -87,11 +87,11 @@
return;
}
if (type == BiometricSourceType.FACE) {
- mKeyguardUpdateMonitor.onFaceAuthenticated(KeyguardUpdateMonitor.getCurrentUser(),
+ mKeyguardUpdateMonitor.onFaceAuthenticated(mSelectedUserInteractor.getSelectedUserId(),
true);
} else if (type == BiometricSourceType.FINGERPRINT) {
mKeyguardUpdateMonitor.onFingerprintAuthenticated(
- KeyguardUpdateMonitor.getCurrentUser(), true);
+ mSelectedUserInteractor.getSelectedUserId(), true);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 0e339dd..9305ab6 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -28,17 +28,17 @@
import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.res.R;
import com.android.systemui.assist.ui.DefaultUiController;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.res.R;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.settings.SecureSettings;
import dagger.Lazy;
@@ -144,6 +144,7 @@
private final UserTracker mUserTracker;
private final DisplayTracker mDisplayTracker;
private final SecureSettings mSecureSettings;
+ private final SelectedUserInteractor mSelectedUserInteractor;
private final DeviceProvisionedController mDeviceProvisionedController;
@@ -152,16 +153,16 @@
private final IVisualQueryDetectionAttentionListener mVisualQueryDetectionAttentionListener =
new IVisualQueryDetectionAttentionListener.Stub() {
- @Override
- public void onAttentionGained() {
- handleVisualAttentionChanged(true);
- }
+ @Override
+ public void onAttentionGained() {
+ handleVisualAttentionChanged(true);
+ }
- @Override
- public void onAttentionLost() {
- handleVisualAttentionChanged(false);
- }
- };
+ @Override
+ public void onAttentionLost() {
+ handleVisualAttentionChanged(false);
+ }
+ };
private final CommandQueue mCommandQueue;
protected final AssistUtils mAssistUtils;
@@ -183,7 +184,8 @@
@Main Handler uiHandler,
UserTracker userTracker,
DisplayTracker displayTracker,
- SecureSettings secureSettings) {
+ SecureSettings secureSettings,
+ SelectedUserInteractor selectedUserInteractor) {
mContext = context;
mDeviceProvisionedController = controller;
mCommandQueue = commandQueue;
@@ -195,6 +197,7 @@
mUserTracker = userTracker;
mDisplayTracker = displayTracker;
mSecureSettings = secureSettings;
+ mSelectedUserInteractor = selectedUserInteractor;
registerVoiceInteractionSessionListener();
registerVisualQueryRecognitionStatusListener();
@@ -316,12 +319,13 @@
public boolean shouldOverrideAssist(int invocationType) {
return mAssistOverrideInvocationTypes != null
&& Arrays.stream(mAssistOverrideInvocationTypes).anyMatch(
- override -> override == invocationType);
+ override -> override == invocationType);
}
/**
* @param invocationTypes The invocation types that will henceforth be handled via
- * OverviewProxy (Launcher); other invocation types should be handled by this class.
+ * OverviewProxy (Launcher); other invocation types should be handled by
+ * this class.
*/
public void setAssistantOverridesRequested(int[] invocationTypes) {
mAssistOverrideInvocationTypes = invocationTypes;
@@ -478,7 +482,7 @@
@Nullable
private ComponentName getAssistInfo() {
- return getAssistInfoForUser(KeyguardUpdateMonitor.getCurrentUser());
+ return getAssistInfoForUser(mSelectedUserInteractor.getSelectedUserId());
}
public void showDisclosure() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt
deleted file mode 100644
index 3f2da5e..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.biometrics
-
-import android.content.Context
-import android.graphics.drawable.Drawable
-import android.util.Log
-import com.airbnb.lottie.LottieAnimationView
-import com.android.systemui.res.R
-import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState
-
-private const val TAG = "AuthBiometricFaceIconController"
-
-/** Face only icon animator for BiometricPrompt. */
-class AuthBiometricFaceIconController(
- context: Context,
- iconView: LottieAnimationView
-) : AuthIconController(context, iconView) {
-
- // false = dark to light, true = light to dark
- private var lastPulseLightToDark = false
-
- private var state: BiometricState = BiometricState.STATE_IDLE
-
- init {
- val size = context.resources.getDimensionPixelSize(R.dimen.biometric_dialog_face_icon_size)
- iconView.layoutParams.width = size
- iconView.layoutParams.height = size
- showStaticDrawable(R.drawable.face_dialog_pulse_dark_to_light)
- }
-
- private fun startPulsing() {
- lastPulseLightToDark = false
- animateIcon(R.drawable.face_dialog_pulse_dark_to_light, true)
- }
-
- private fun pulseInNextDirection() {
- val iconRes = if (lastPulseLightToDark) {
- R.drawable.face_dialog_pulse_dark_to_light
- } else {
- R.drawable.face_dialog_pulse_light_to_dark
- }
- animateIcon(iconRes, true /* repeat */)
- lastPulseLightToDark = !lastPulseLightToDark
- }
-
- override fun handleAnimationEnd(drawable: Drawable) {
- if (state == BiometricState.STATE_AUTHENTICATING || state == BiometricState.STATE_HELP) {
- pulseInNextDirection()
- }
- }
-
- override fun updateIcon(oldState: BiometricState, newState: BiometricState) {
- val lastStateIsErrorIcon = (oldState == BiometricState.STATE_ERROR || oldState == BiometricState.STATE_HELP)
- if (newState == BiometricState.STATE_AUTHENTICATING_ANIMATING_IN) {
- showStaticDrawable(R.drawable.face_dialog_pulse_dark_to_light)
- iconView.contentDescription = context.getString(
- R.string.biometric_dialog_face_icon_description_authenticating
- )
- } else if (newState == BiometricState.STATE_AUTHENTICATING) {
- startPulsing()
- iconView.contentDescription = context.getString(
- R.string.biometric_dialog_face_icon_description_authenticating
- )
- } else if (oldState == BiometricState.STATE_PENDING_CONFIRMATION && newState == BiometricState.STATE_AUTHENTICATED) {
- animateIconOnce(R.drawable.face_dialog_dark_to_checkmark)
- iconView.contentDescription = context.getString(
- R.string.biometric_dialog_face_icon_description_confirmed
- )
- } else if (lastStateIsErrorIcon && newState == BiometricState.STATE_IDLE) {
- animateIconOnce(R.drawable.face_dialog_error_to_idle)
- iconView.contentDescription = context.getString(
- R.string.biometric_dialog_face_icon_description_idle
- )
- } else if (lastStateIsErrorIcon && newState == BiometricState.STATE_AUTHENTICATED) {
- animateIconOnce(R.drawable.face_dialog_dark_to_checkmark)
- iconView.contentDescription = context.getString(
- R.string.biometric_dialog_face_icon_description_authenticated
- )
- } else if (newState == BiometricState.STATE_ERROR && oldState != BiometricState.STATE_ERROR) {
- animateIconOnce(R.drawable.face_dialog_dark_to_error)
- iconView.contentDescription = context.getString(
- R.string.keyguard_face_failed
- )
- } else if (oldState == BiometricState.STATE_AUTHENTICATING && newState == BiometricState.STATE_AUTHENTICATED) {
- animateIconOnce(R.drawable.face_dialog_dark_to_checkmark)
- iconView.contentDescription = context.getString(
- R.string.biometric_dialog_face_icon_description_authenticated
- )
- } else if (newState == BiometricState.STATE_PENDING_CONFIRMATION) {
- animateIconOnce(R.drawable.face_dialog_wink_from_dark)
- iconView.contentDescription = context.getString(
- R.string.biometric_dialog_face_icon_description_authenticated
- )
- } else if (newState == BiometricState.STATE_IDLE) {
- showStaticDrawable(R.drawable.face_dialog_idle_static)
- iconView.contentDescription = context.getString(
- R.string.biometric_dialog_face_icon_description_idle
- )
- } else {
- Log.w(TAG, "Unhandled state: $newState")
- }
- state = newState
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
deleted file mode 100644
index 09eabf2..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.annotation.RawRes
-import android.content.Context
-import com.airbnb.lottie.LottieAnimationView
-import com.android.systemui.res.R
-import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState
-import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_AUTHENTICATED
-import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_ERROR
-import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_HELP
-import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_PENDING_CONFIRMATION
-
-/** Face/Fingerprint combined icon animator for BiometricPrompt. */
-open class AuthBiometricFingerprintAndFaceIconController(
- context: Context,
- iconView: LottieAnimationView,
- iconViewOverlay: LottieAnimationView,
-) : AuthBiometricFingerprintIconController(context, iconView, iconViewOverlay) {
-
- override val actsAsConfirmButton: Boolean = true
-
- override fun shouldAnimateIconViewForTransition(
- oldState: BiometricState,
- newState: BiometricState
- ): Boolean = when (newState) {
- STATE_PENDING_CONFIRMATION -> true
- else -> super.shouldAnimateIconViewForTransition(oldState, newState)
- }
-
- @RawRes
- override fun getAnimationForTransition(
- oldState: BiometricState,
- newState: BiometricState
- ): Int? = when (newState) {
- STATE_AUTHENTICATED -> {
- if (oldState == STATE_PENDING_CONFIRMATION) {
- R.raw.fingerprint_dialogue_unlocked_to_checkmark_success_lottie
- } else {
- super.getAnimationForTransition(oldState, newState)
- }
- }
- STATE_PENDING_CONFIRMATION -> {
- if (oldState == STATE_ERROR || oldState == STATE_HELP) {
- R.raw.fingerprint_dialogue_error_to_unlock_lottie
- } else {
- R.raw.fingerprint_dialogue_fingerprint_to_unlock_lottie
- }
- }
- else -> super.getAnimationForTransition(oldState, newState)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
deleted file mode 100644
index 0ad3848..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.annotation.RawRes
-import android.content.Context
-import android.content.Context.FINGERPRINT_SERVICE
-import android.hardware.fingerprint.FingerprintManager
-import android.view.DisplayInfo
-import android.view.Surface
-import android.view.View
-import androidx.annotation.VisibleForTesting
-import com.airbnb.lottie.LottieAnimationView
-import com.android.settingslib.widget.LottieColorUtils
-import com.android.systemui.res.R
-import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState
-import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_AUTHENTICATED
-import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_AUTHENTICATING
-import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_AUTHENTICATING_ANIMATING_IN
-import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_ERROR
-import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_HELP
-import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_IDLE
-import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState.STATE_PENDING_CONFIRMATION
-
-
-/** Fingerprint only icon animator for BiometricPrompt. */
-open class AuthBiometricFingerprintIconController(
- context: Context,
- iconView: LottieAnimationView,
- protected val iconViewOverlay: LottieAnimationView
-) : AuthIconController(context, iconView) {
-
- private val isSideFps: Boolean
- private val isReverseDefaultRotation =
- context.resources.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)
-
- var iconLayoutParamSize: Pair<Int, Int> = Pair(1, 1)
- set(value) {
- if (field == value) {
- return
- }
- iconViewOverlay.layoutParams.width = value.first
- iconViewOverlay.layoutParams.height = value.second
- iconView.layoutParams.width = value.first
- iconView.layoutParams.height = value.second
- field = value
- }
-
- init {
- iconLayoutParamSize = Pair(context.resources.getDimensionPixelSize(
- R.dimen.biometric_dialog_fingerprint_icon_width),
- context.resources.getDimensionPixelSize(
- R.dimen.biometric_dialog_fingerprint_icon_height))
- isSideFps =
- (context.getSystemService(FINGERPRINT_SERVICE) as FingerprintManager?)?.let { fpm ->
- fpm.sensorPropertiesInternal.any { it.isAnySidefpsType }
- } ?: false
- preloadAssets(context)
- val displayInfo = DisplayInfo()
- context.display?.getDisplayInfo(displayInfo)
- if (isSideFps && getRotationFromDefault(displayInfo.rotation) == Surface.ROTATION_180) {
- iconView.rotation = 180f
- }
- }
-
- private fun updateIconSideFps(lastState: BiometricState, newState: BiometricState) {
- val displayInfo = DisplayInfo()
- context.display?.getDisplayInfo(displayInfo)
- val rotation = getRotationFromDefault(displayInfo.rotation)
- val iconViewOverlayAnimation =
- getSideFpsOverlayAnimationForTransition(lastState, newState, rotation) ?: return
-
- if (!(lastState == STATE_AUTHENTICATING_ANIMATING_IN && newState == STATE_AUTHENTICATING)) {
- iconViewOverlay.setAnimation(iconViewOverlayAnimation)
- }
-
- val iconContentDescription = getIconContentDescription(newState)
- if (iconContentDescription != null) {
- iconView.contentDescription = iconContentDescription
- }
-
- iconView.frame = 0
- iconViewOverlay.frame = 0
- if (shouldAnimateSfpsIconViewForTransition(lastState, newState)) {
- iconView.playAnimation()
- }
-
- if (shouldAnimateIconViewOverlayForTransition(lastState, newState)) {
- iconViewOverlay.playAnimation()
- }
-
- LottieColorUtils.applyDynamicColors(context, iconView)
- LottieColorUtils.applyDynamicColors(context, iconViewOverlay)
- }
-
- private fun updateIconNormal(lastState: BiometricState, newState: BiometricState) {
- val icon = getAnimationForTransition(lastState, newState) ?: return
-
- if (!(lastState == STATE_AUTHENTICATING_ANIMATING_IN && newState == STATE_AUTHENTICATING)) {
- iconView.setAnimation(icon)
- }
-
- val iconContentDescription = getIconContentDescription(newState)
- if (iconContentDescription != null) {
- iconView.contentDescription = iconContentDescription
- }
-
- iconView.frame = 0
- if (shouldAnimateIconViewForTransition(lastState, newState)) {
- iconView.playAnimation()
- }
- LottieColorUtils.applyDynamicColors(context, iconView)
- }
-
- override fun updateIcon(lastState: BiometricState, newState: BiometricState) {
- if (isSideFps) {
- updateIconSideFps(lastState, newState)
- } else {
- iconViewOverlay.visibility = View.GONE
- updateIconNormal(lastState, newState)
- }
- }
-
- @VisibleForTesting
- fun getIconContentDescription(newState: BiometricState): CharSequence? {
- val id = when (newState) {
- STATE_IDLE,
- STATE_AUTHENTICATING_ANIMATING_IN,
- STATE_AUTHENTICATING,
- STATE_AUTHENTICATED ->
- if (isSideFps) {
- R.string.security_settings_sfps_enroll_find_sensor_message
- } else {
- R.string.fingerprint_dialog_touch_sensor
- }
- STATE_PENDING_CONFIRMATION ->
- if (isSideFps) {
- R.string.security_settings_sfps_enroll_find_sensor_message
- } else {
- R.string.fingerprint_dialog_authenticated_confirmation
- }
- STATE_ERROR,
- STATE_HELP -> R.string.biometric_dialog_try_again
- else -> null
- }
- return if (id != null) context.getString(id) else null
- }
-
- protected open fun shouldAnimateIconViewForTransition(
- oldState: BiometricState,
- newState: BiometricState
- ) = when (newState) {
- STATE_HELP,
- STATE_ERROR -> true
- STATE_AUTHENTICATING_ANIMATING_IN,
- STATE_AUTHENTICATING -> oldState == STATE_ERROR || oldState == STATE_HELP
- STATE_AUTHENTICATED -> true
- else -> false
- }
-
- private fun shouldAnimateSfpsIconViewForTransition(
- oldState: BiometricState,
- newState: BiometricState
- ) = when (newState) {
- STATE_HELP,
- STATE_ERROR -> true
- STATE_AUTHENTICATING_ANIMATING_IN,
- STATE_AUTHENTICATING ->
- oldState == STATE_ERROR || oldState == STATE_HELP || oldState == STATE_IDLE
- STATE_AUTHENTICATED -> true
- else -> false
- }
-
- protected open fun shouldAnimateIconViewOverlayForTransition(
- oldState: BiometricState,
- newState: BiometricState
- ) = when (newState) {
- STATE_HELP,
- STATE_ERROR -> true
- STATE_AUTHENTICATING_ANIMATING_IN,
- STATE_AUTHENTICATING -> oldState == STATE_ERROR || oldState == STATE_HELP
- STATE_AUTHENTICATED -> true
- else -> false
- }
-
- @RawRes
- protected open fun getAnimationForTransition(
- oldState: BiometricState,
- newState: BiometricState
- ): Int? {
- val id = when (newState) {
- STATE_HELP,
- STATE_ERROR -> {
- R.raw.fingerprint_dialogue_fingerprint_to_error_lottie
- }
- STATE_AUTHENTICATING_ANIMATING_IN,
- STATE_AUTHENTICATING -> {
- if (oldState == STATE_ERROR || oldState == STATE_HELP) {
- R.raw.fingerprint_dialogue_error_to_fingerprint_lottie
- } else {
- R.raw.fingerprint_dialogue_fingerprint_to_error_lottie
- }
- }
- STATE_AUTHENTICATED -> {
- if (oldState == STATE_ERROR || oldState == STATE_HELP) {
- R.raw.fingerprint_dialogue_error_to_success_lottie
- } else {
- R.raw.fingerprint_dialogue_fingerprint_to_success_lottie
- }
- }
- else -> return null
- }
- return if (id != null) return id else null
- }
-
- private fun getRotationFromDefault(rotation: Int): Int =
- if (isReverseDefaultRotation) (rotation + 1) % 4 else rotation
-
- @RawRes
- private fun getSideFpsOverlayAnimationForTransition(
- oldState: BiometricState,
- newState: BiometricState,
- rotation: Int
- ): Int? = when (newState) {
- STATE_HELP,
- STATE_ERROR -> {
- when (rotation) {
- Surface.ROTATION_0 -> R.raw.biometricprompt_fingerprint_to_error_landscape
- Surface.ROTATION_90 ->
- R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft
- Surface.ROTATION_180 ->
- R.raw.biometricprompt_fingerprint_to_error_landscape
- Surface.ROTATION_270 ->
- R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright
- else -> R.raw.biometricprompt_fingerprint_to_error_landscape
- }
- }
- STATE_AUTHENTICATING_ANIMATING_IN,
- STATE_AUTHENTICATING -> {
- if (oldState == STATE_ERROR || oldState == STATE_HELP) {
- when (rotation) {
- Surface.ROTATION_0 ->
- R.raw.biometricprompt_symbol_error_to_fingerprint_landscape
- Surface.ROTATION_90 ->
- R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_topleft
- Surface.ROTATION_180 ->
- R.raw.biometricprompt_symbol_error_to_fingerprint_landscape
- Surface.ROTATION_270 ->
- R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_bottomright
- else -> R.raw.biometricprompt_symbol_error_to_fingerprint_landscape
- }
- } else {
- when (rotation) {
- Surface.ROTATION_0 -> R.raw.biometricprompt_fingerprint_to_error_landscape
- Surface.ROTATION_90 ->
- R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft
- Surface.ROTATION_180 ->
- R.raw.biometricprompt_fingerprint_to_error_landscape
- Surface.ROTATION_270 ->
- R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright
- else -> R.raw.biometricprompt_fingerprint_to_error_landscape
- }
- }
- }
- STATE_AUTHENTICATED -> {
- if (oldState == STATE_ERROR || oldState == STATE_HELP) {
- when (rotation) {
- Surface.ROTATION_0 ->
- R.raw.biometricprompt_symbol_error_to_success_landscape
- Surface.ROTATION_90 ->
- R.raw.biometricprompt_symbol_error_to_success_portrait_topleft
- Surface.ROTATION_180 ->
- R.raw.biometricprompt_symbol_error_to_success_landscape
- Surface.ROTATION_270 ->
- R.raw.biometricprompt_symbol_error_to_success_portrait_bottomright
- else -> R.raw.biometricprompt_symbol_error_to_success_landscape
- }
- } else {
- when (rotation) {
- Surface.ROTATION_0 ->
- R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
- Surface.ROTATION_90 ->
- R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_topleft
- Surface.ROTATION_180 ->
- R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
- Surface.ROTATION_270 ->
- R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_bottomright
- else -> R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
- }
- }
- }
- else -> null
- }
-
- private fun preloadAssets(context: Context) {
- if (isSideFps) {
- cacheLottieAssetsInContext(
- context,
- R.raw.biometricprompt_fingerprint_to_error_landscape,
- R.raw.biometricprompt_folded_base_bottomright,
- R.raw.biometricprompt_folded_base_default,
- R.raw.biometricprompt_folded_base_topleft,
- R.raw.biometricprompt_landscape_base,
- R.raw.biometricprompt_portrait_base_bottomright,
- R.raw.biometricprompt_portrait_base_topleft,
- R.raw.biometricprompt_symbol_error_to_fingerprint_landscape,
- R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_bottomright,
- R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_topleft,
- R.raw.biometricprompt_symbol_error_to_success_landscape,
- R.raw.biometricprompt_symbol_error_to_success_portrait_bottomright,
- R.raw.biometricprompt_symbol_error_to_success_portrait_topleft,
- R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright,
- R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft,
- R.raw.biometricprompt_symbol_fingerprint_to_success_landscape,
- R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_bottomright,
- R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_topleft
- )
- } else {
- cacheLottieAssetsInContext(
- context,
- R.raw.fingerprint_dialogue_error_to_fingerprint_lottie,
- R.raw.fingerprint_dialogue_error_to_success_lottie,
- R.raw.fingerprint_dialogue_fingerprint_to_error_lottie,
- R.raw.fingerprint_dialogue_fingerprint_to_success_lottie
- )
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
index 054bd08..8d1d905 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.biometrics
import android.annotation.MainThread
@@ -25,7 +41,7 @@
shadeExpansionCollectorJob =
scope.launch {
// wait for it to emit true once
- shadeInteractorLazy.get().isAnyExpanding.first { it }
+ shadeInteractorLazy.get().isUserInteracting.first { it }
onShadeInteraction.run()
}
shadeExpansionCollectorJob?.invokeOnCompletion { shadeExpansionCollectorJob = null }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt
deleted file mode 100644
index 958213a..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthIconController.kt
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.annotation.DrawableRes
-import android.content.Context
-import android.graphics.drawable.Animatable2
-import android.graphics.drawable.AnimatedVectorDrawable
-import android.graphics.drawable.Drawable
-import android.util.Log
-import com.airbnb.lottie.LottieAnimationView
-import com.airbnb.lottie.LottieCompositionFactory
-import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState
-
-private const val TAG = "AuthIconController"
-
-/** Controller for animating the BiometricPrompt icon/affordance. */
-abstract class AuthIconController(
- protected val context: Context,
- protected val iconView: LottieAnimationView
-) : Animatable2.AnimationCallback() {
-
- /** If this controller should ignore events and pause. */
- var deactivated: Boolean = false
-
- /** If the icon view should be treated as an alternate "confirm" button. */
- open val actsAsConfirmButton: Boolean = false
-
- final override fun onAnimationStart(drawable: Drawable) {
- super.onAnimationStart(drawable)
- }
-
- final override fun onAnimationEnd(drawable: Drawable) {
- super.onAnimationEnd(drawable)
-
- if (!deactivated) {
- handleAnimationEnd(drawable)
- }
- }
-
- /** Set the icon to a static image. */
- protected fun showStaticDrawable(@DrawableRes iconRes: Int) {
- iconView.setImageDrawable(context.getDrawable(iconRes))
- }
-
- /** Animate a resource. */
- protected fun animateIconOnce(@DrawableRes iconRes: Int) {
- animateIcon(iconRes, false)
- }
-
- /** Animate a resource. */
- protected fun animateIcon(@DrawableRes iconRes: Int, repeat: Boolean) {
- if (!deactivated) {
- val icon = context.getDrawable(iconRes) as AnimatedVectorDrawable
- iconView.setImageDrawable(icon)
- icon.forceAnimationOnUI()
- if (repeat) {
- icon.registerAnimationCallback(this)
- }
- icon.start()
- }
- }
-
- /** Update the icon to reflect the [newState]. */
- fun updateState(lastState: BiometricState, newState: BiometricState) {
- if (deactivated) {
- Log.w(TAG, "Ignoring updateState when deactivated: $newState")
- } else {
- updateIcon(lastState, newState)
- }
- }
-
- /** Call during [updateState] if the controller is not [deactivated]. */
- abstract fun updateIcon(lastState: BiometricState, newState: BiometricState)
-
- /** Called during [onAnimationEnd] if the controller is not [deactivated]. */
- open fun handleAnimationEnd(drawable: Drawable) {}
-
- // TODO(b/251476085): Migrate this to an extension at the appropriate level?
- /** Load the given [rawResources] immediately so they are cached for use in the [context]. */
- protected fun cacheLottieAssetsInContext(context: Context, vararg rawResources: Int) {
- for (res in rawResources) {
- LottieCompositionFactory.fromRawRes(context, res)
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index c9e4cbe..92eacf1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -97,6 +97,7 @@
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.Execution;
import com.android.systemui.util.time.SystemClock;
@@ -167,6 +168,7 @@
@NonNull private final AlternateBouncerInteractor mAlternateBouncerInteractor;
@NonNull private final InputManager mInputManager;
@NonNull private final UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
+ @NonNull private final SelectedUserInteractor mSelectedUserInteractor;
private final boolean mIgnoreRefreshRate;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
@@ -281,7 +283,8 @@
mPrimaryBouncerInteractor,
mAlternateBouncerInteractor,
mUdfpsKeyguardAccessibilityDelegate,
- mUdfpsKeyguardViewModels
+ mUdfpsKeyguardViewModels,
+ mSelectedUserInteractor
)));
}
@@ -644,7 +647,8 @@
@NonNull InputManager inputManager,
@NonNull KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
@NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate,
- @NonNull Provider<UdfpsKeyguardViewModels> udfpsKeyguardViewModelsProvider) {
+ @NonNull Provider<UdfpsKeyguardViewModels> udfpsKeyguardViewModelsProvider,
+ @NonNull SelectedUserInteractor selectedUserInteractor) {
mContext = context;
mExecution = execution;
mVibrator = vibrator;
@@ -687,6 +691,7 @@
mAlternateBouncerInteractor = alternateBouncerInteractor;
mInputManager = inputManager;
mUdfpsKeyguardAccessibilityDelegate = udfpsKeyguardAccessibilityDelegate;
+ mSelectedUserInteractor = selectedUserInteractor;
mTouchProcessor = singlePointerTouchProcessor;
mSessionTracker = sessionTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 7130bfb..272e0f2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -44,7 +44,6 @@
import androidx.annotation.LayoutRes
import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.res.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.biometrics.ui.controller.UdfpsKeyguardViewController
@@ -56,12 +55,14 @@
import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
import javax.inject.Provider
@@ -78,31 +79,32 @@
@ExperimentalCoroutinesApi
@UiThread
class UdfpsControllerOverlay @JvmOverloads constructor(
- private val context: Context,
- private val inflater: LayoutInflater,
- private val windowManager: WindowManager,
- private val accessibilityManager: AccessibilityManager,
- private val statusBarStateController: StatusBarStateController,
- private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val dialogManager: SystemUIDialogManager,
- private val dumpManager: DumpManager,
- private val transitionController: LockscreenShadeTransitionController,
- private val configurationController: ConfigurationController,
- private val keyguardStateController: KeyguardStateController,
- private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
- private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider,
- val requestId: Long,
- @ShowReason val requestReason: Int,
- private val controllerCallback: IUdfpsOverlayControllerCallback,
- private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
- private val activityLaunchAnimator: ActivityLaunchAnimator,
- private val featureFlags: FeatureFlags,
- private val primaryBouncerInteractor: PrimaryBouncerInteractor,
- private val alternateBouncerInteractor: AlternateBouncerInteractor,
- private val isDebuggable: Boolean = Build.IS_DEBUGGABLE,
- private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
- private val udfpsKeyguardViewModels: Provider<UdfpsKeyguardViewModels>,
+ private val context: Context,
+ private val inflater: LayoutInflater,
+ private val windowManager: WindowManager,
+ private val accessibilityManager: AccessibilityManager,
+ private val statusBarStateController: StatusBarStateController,
+ private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val dialogManager: SystemUIDialogManager,
+ private val dumpManager: DumpManager,
+ private val transitionController: LockscreenShadeTransitionController,
+ private val configurationController: ConfigurationController,
+ private val keyguardStateController: KeyguardStateController,
+ private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
+ private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider,
+ val requestId: Long,
+ @ShowReason val requestReason: Int,
+ private val controllerCallback: IUdfpsOverlayControllerCallback,
+ private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
+ private val activityLaunchAnimator: ActivityLaunchAnimator,
+ private val featureFlags: FeatureFlags,
+ private val primaryBouncerInteractor: PrimaryBouncerInteractor,
+ private val alternateBouncerInteractor: AlternateBouncerInteractor,
+ private val isDebuggable: Boolean = Build.IS_DEBUGGABLE,
+ private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
+ private val udfpsKeyguardViewModels: Provider<UdfpsKeyguardViewModels>,
+ private val selectedUserInteractor: SelectedUserInteractor,
) {
/** The view, when [isShowing], or null. */
var overlayView: UdfpsView? = null
@@ -268,6 +270,7 @@
primaryBouncerInteractor,
alternateBouncerInteractor,
udfpsKeyguardAccessibilityDelegate,
+ selectedUserInteractor,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 3d5be6f..d7df0e5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -45,6 +45,7 @@
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import java.io.PrintWriter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -68,6 +69,7 @@
primaryBouncerInteractor: PrimaryBouncerInteractor,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
+ private val selectedUserInteractor: SelectedUserInteractor,
) :
UdfpsAnimationViewController<UdfpsKeyguardViewLegacy>(
view,
@@ -384,7 +386,7 @@
}
if (
keyguardUpdateMonitor.getUserUnlockedWithBiometric(
- KeyguardUpdateMonitor.getCurrentUser()
+ selectedUserInteractor.getSelectedUserId()
)
) {
// If the device was unlocked by a biometric, immediately hide the UDFPS icon to avoid
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
index c4c52e8b..050b399 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
@@ -42,8 +42,11 @@
/** Repository for the current state of the display */
interface DisplayStateRepository {
/**
- * Whether or not the direction rotation is applied to get to an application's requested
- * orientation is reversed.
+ * If true, the direction rotation is applied to get to an application's requested orientation
+ * is reversed. Normally, the model is that landscape is clockwise from portrait; thus on a
+ * portrait device an app requesting landscape will cause a clockwise rotation, and on a
+ * landscape device an app requesting portrait will cause a counter-clockwise rotation. Setting
+ * true here reverses that logic. See go/natural-orientation for context.
*/
val isReverseDefaultRotation: Boolean
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
index a317a06..427361d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
@@ -57,6 +57,15 @@
/** Display change event indicating a change to the given displayId has occurred. */
val displayChanges: Flow<Int>
+ /**
+ * If true, the direction rotation is applied to get to an application's requested orientation
+ * is reversed. Normally, the model is that landscape is clockwise from portrait; thus on a
+ * portrait device an app requesting landscape will cause a clockwise rotation, and on a
+ * landscape device an app requesting portrait will cause a counter-clockwise rotation. Setting
+ * true here reverses that logic. See go/natural-orientation for context.
+ */
+ val isReverseDefaultRotation: Boolean
+
/** Called on configuration changes, used to keep the display state in sync */
fun onConfigurationChanged(newConfig: Configuration)
}
@@ -112,6 +121,8 @@
override val currentRotation: StateFlow<DisplayRotation> =
displayStateRepository.currentRotation
+ override val isReverseDefaultRotation: Boolean = displayStateRepository.isReverseDefaultRotation
+
override fun onConfigurationChanged(newConfig: Configuration) {
screenSizeFoldProvider.onConfigurationChange(newConfig)
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
index 2a1047a..38043b4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
@@ -17,13 +17,13 @@
package com.android.systemui.biometrics.domain.interactor
import android.view.MotionEvent
-import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
@@ -35,11 +35,16 @@
@SysUISingleton
class UdfpsOverlayInteractor
@Inject
-constructor(private val authController: AuthController, @Application scope: CoroutineScope) {
+constructor(
+ private val authController: AuthController,
+ private val selectedUserInteractor: SelectedUserInteractor,
+ @Application scope: CoroutineScope
+) {
/** Whether a touch is within the under-display fingerprint sensor area */
fun isTouchWithinUdfpsArea(ev: MotionEvent): Boolean {
- val isUdfpsEnrolled = authController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())
+ val isUdfpsEnrolled =
+ authController.isUdfpsEnrolled(selectedUserInteractor.getSelectedUserId())
val isWithinOverlayBounds =
udfpsOverlayParams.value.overlayBounds.contains(ev.rawX.toInt(), ev.rawY.toInt())
return isUdfpsEnrolled && isWithinOverlayBounds
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java b/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java
index cef0be0..0d72b9c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java
@@ -29,11 +29,10 @@
import android.widget.LinearLayout;
import android.widget.TextView;
-import com.android.systemui.res.R;
-import com.android.systemui.biometrics.AuthBiometricFingerprintIconController;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.AuthDialog;
import com.android.systemui.biometrics.UdfpsDialogMeasureAdapter;
+import com.android.systemui.res.R;
import kotlin.Pair;
@@ -85,13 +84,13 @@
}
@Deprecated
- public void updateFingerprintAffordanceSize(
- @NonNull AuthBiometricFingerprintIconController iconController) {
+ public Pair<Integer, Integer> getUpdatedFingerprintAffordanceSize() {
if (mUdfpsAdapter != null) {
final int sensorDiameter = mUdfpsAdapter.getSensorDiameter(
mScaleFactorProvider.provide());
- iconController.setIconLayoutParamSize(new Pair(sensorDiameter, sensorDiameter));
+ return new Pair(sensorDiameter, sensorDiameter);
}
+ return null;
}
@NonNull
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index c29efc0..ac48b6a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -38,11 +38,7 @@
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.airbnb.lottie.LottieAnimationView
-import com.android.systemui.res.R
-import com.android.systemui.biometrics.AuthBiometricFaceIconController
-import com.android.systemui.biometrics.AuthBiometricFingerprintAndFaceIconController
-import com.android.systemui.biometrics.AuthBiometricFingerprintIconController
-import com.android.systemui.biometrics.AuthIconController
+import com.airbnb.lottie.LottieCompositionFactory
import com.android.systemui.biometrics.AuthPanelController
import com.android.systemui.biometrics.shared.model.BiometricModalities
import com.android.systemui.biometrics.shared.model.BiometricModality
@@ -56,6 +52,7 @@
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
@@ -101,10 +98,15 @@
!accessibilityManager.isEnabled || !accessibilityManager.isTouchExplorationEnabled
descriptionView.movementMethod = ScrollingMovementMethod()
- val iconViewOverlay = view.requireViewById<LottieAnimationView>(R.id.biometric_icon_overlay)
+ val iconOverlayView = view.requireViewById<LottieAnimationView>(R.id.biometric_icon_overlay)
val iconView = view.requireViewById<LottieAnimationView>(R.id.biometric_icon)
- PromptFingerprintIconViewBinder.bind(iconView, viewModel.fingerprintIconViewModel)
+ PromptIconViewBinder.bind(
+ iconView,
+ iconOverlayView,
+ view.getUpdatedFingerprintAffordanceSize(),
+ viewModel.iconViewModel
+ )
val indicatorMessageView = view.requireViewById<TextView>(R.id.indicator)
@@ -128,9 +130,21 @@
// bind to prompt
var boundSize = false
+
view.repeatWhenAttached {
// these do not change and need to be set before any size transitions
val modalities = viewModel.modalities.first()
+ if (modalities.hasFingerprint) {
+ /**
+ * Load the given [rawResources] immediately so they are cached for use in the
+ * [context].
+ */
+ val rawResources = viewModel.iconViewModel.getRawAssets(modalities.hasSfps)
+ for (res in rawResources) {
+ LottieCompositionFactory.fromRawRes(view.context, res)
+ }
+ }
+
titleView.text = viewModel.title.first()
descriptionView.text = viewModel.description.first()
subtitleView.text = viewModel.subtitle.first()
@@ -148,27 +162,8 @@
legacyCallback.onButtonTryAgain()
}
- // TODO(b/251476085): migrate legacy icon controllers and remove
- var legacyState = viewModel.legacyState.value
- val iconController =
- modalities.asIconController(
- view.context,
- iconView,
- iconViewOverlay,
- )
- adapter.attach(this, iconController, modalities, legacyCallback)
- if (iconController is AuthBiometricFingerprintIconController) {
- view.updateFingerprintAffordanceSize(iconController)
- }
- if (iconController is HackyCoexIconController) {
- iconController.faceMode = !viewModel.isConfirmationRequired.first()
- }
+ adapter.attach(this, modalities, legacyCallback)
- // the icon controller must be created before this happens for the legacy
- // sizing code in BiometricPromptLayout to work correctly. Simplify this
- // when those are also migrated. (otherwise the icon size may not be set to
- // a pixel value before the view is measured and WRAP_CONTENT will be incorrectly
- // used as part of the measure spec)
if (!boundSize) {
boundSize = true
BiometricViewSizeBinder.bind(
@@ -212,14 +207,6 @@
) {
legacyCallback.onStartDelayedFingerprintSensor()
}
-
- if (newMode.isStarted) {
- // do wonky switch from implicit to explicit flow
- (iconController as? HackyCoexIconController)?.faceMode = false
- viewModel.showAuthenticating(
- modalities.asDefaultHelpMessage(view.context),
- )
- }
}
}
@@ -312,7 +299,7 @@
viewModel.isIconConfirmButton
.map { isPending ->
when {
- isPending && iconController.actsAsConfirmButton ->
+ isPending && modalities.hasFaceAndFingerprint ->
View.OnTouchListener { _: View, event: MotionEvent ->
viewModel.onOverlayTouch(event)
}
@@ -320,22 +307,11 @@
}
}
.collect { onTouch ->
- iconViewOverlay.setOnTouchListener(onTouch)
+ iconOverlayView.setOnTouchListener(onTouch)
iconView.setOnTouchListener(onTouch)
}
}
- // TODO(b/251476085): remove w/ legacy icon controllers
- // set icon affordance using legacy states
- // like the old code, this causes animations to repeat on config changes :(
- // but keep behavior for now as no one has complained...
- launch {
- viewModel.legacyState.collect { newState ->
- iconController.updateState(legacyState, newState)
- legacyState = newState
- }
- }
-
// dismiss prompt when authenticated and confirmed
launch {
viewModel.isAuthenticated.collect { authState ->
@@ -350,7 +326,7 @@
// Allow icon to be used as confirmation button with a11y enabled
if (accessibilityManager.isTouchExplorationEnabled) {
- iconViewOverlay.setOnClickListener {
+ iconOverlayView.setOnClickListener {
viewModel.confirmAuthenticated()
}
iconView.setOnClickListener { viewModel.confirmAuthenticated() }
@@ -377,7 +353,6 @@
launch {
viewModel.message.collect { promptMessage ->
val isError = promptMessage is PromptMessage.Error
-
indicatorMessageView.text = promptMessage.message
indicatorMessageView.setTextColor(
if (isError) textColorError else textColorHint
@@ -472,9 +447,6 @@
private var modalities: BiometricModalities = BiometricModalities()
private var legacyCallback: Callback? = null
- var legacyIconController: AuthIconController? = null
- private set
-
// hacky way to suppress lockout errors
private val lockoutErrorStrings =
listOf(
@@ -485,24 +457,20 @@
fun attach(
lifecycleOwner: LifecycleOwner,
- iconController: AuthIconController,
activeModalities: BiometricModalities,
callback: Callback,
) {
modalities = activeModalities
- legacyIconController = iconController
legacyCallback = callback
lifecycleOwner.lifecycle.addObserver(
object : DefaultLifecycleObserver {
override fun onCreate(owner: LifecycleOwner) {
lifecycleScope = owner.lifecycleScope
- iconController.deactivated = false
}
override fun onDestroy(owner: LifecycleOwner) {
lifecycleScope = null
- iconController.deactivated = true
}
}
)
@@ -626,61 +594,9 @@
else -> ""
}
-private fun BiometricModalities.asIconController(
- context: Context,
- iconView: LottieAnimationView,
- iconViewOverlay: LottieAnimationView,
-): AuthIconController =
- when {
- hasFaceAndFingerprint -> HackyCoexIconController(context, iconView, iconViewOverlay)
- hasFingerprint -> AuthBiometricFingerprintIconController(context, iconView, iconViewOverlay)
- hasFace -> AuthBiometricFaceIconController(context, iconView)
- else -> throw IllegalStateException("unexpected view type :$this")
- }
-
private fun Boolean.asVisibleOrGone(): Int = if (this) View.VISIBLE else View.GONE
private fun Boolean.asVisibleOrHidden(): Int = if (this) View.VISIBLE else View.INVISIBLE
// TODO(b/251476085): proper type?
typealias BiometricJankListener = Animator.AnimatorListener
-
-// TODO(b/251476085): delete - temporary until the legacy icon controllers are replaced
-private class HackyCoexIconController(
- context: Context,
- iconView: LottieAnimationView,
- iconViewOverlay: LottieAnimationView,
-) : AuthBiometricFingerprintAndFaceIconController(context, iconView, iconViewOverlay) {
-
- private var state: Spaghetti.BiometricState? = null
- private val faceController = AuthBiometricFaceIconController(context, iconView)
-
- var faceMode: Boolean = true
- set(value) {
- if (field != value) {
- field = value
-
- faceController.deactivated = !value
- iconView.setImageIcon(null)
- iconViewOverlay.setImageIcon(null)
- state?.let { updateIcon(Spaghetti.BiometricState.STATE_IDLE, it) }
- }
- }
-
- override fun updateIcon(
- lastState: Spaghetti.BiometricState,
- newState: Spaghetti.BiometricState,
- ) {
- if (deactivated) {
- return
- }
-
- if (faceMode) {
- faceController.updateIcon(lastState, newState)
- } else {
- super.updateIcon(lastState, newState)
- }
-
- state = newState
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptFingerprintIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptFingerprintIconViewBinder.kt
deleted file mode 100644
index d28f1dc..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptFingerprintIconViewBinder.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.systemui.biometrics.ui.binder
-
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.airbnb.lottie.LottieAnimationView
-import com.android.systemui.biometrics.ui.viewmodel.PromptFingerprintIconViewModel
-import com.android.systemui.lifecycle.repeatWhenAttached
-import kotlinx.coroutines.launch
-
-/** Sub-binder for [BiometricPromptLayout.iconView]. */
-object PromptFingerprintIconViewBinder {
-
- /** Binds [BiometricPromptLayout.iconView] to [PromptFingerprintIconViewModel]. */
- @JvmStatic
- fun bind(view: LottieAnimationView, viewModel: PromptFingerprintIconViewModel) {
- view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- viewModel.onConfigurationChanged(view.context.resources.configuration)
- launch {
- viewModel.iconAsset.collect { iconAsset ->
- if (iconAsset != -1) {
- view.setAnimation(iconAsset)
- // TODO: must replace call below once non-sfps asset logic and
- // shouldAnimateIconView logic is migrated to this ViewModel.
- view.playAnimation()
- }
- }
- }
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
new file mode 100644
index 0000000..475ef18
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.biometrics.ui.binder
+
+import android.graphics.drawable.Animatable2
+import android.graphics.drawable.AnimatedVectorDrawable
+import android.graphics.drawable.Drawable
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.airbnb.lottie.LottieAnimationView
+import com.android.settingslib.widget.LottieColorUtils
+import com.android.systemui.biometrics.ui.viewmodel.PromptIconViewModel
+import com.android.systemui.biometrics.ui.viewmodel.PromptIconViewModel.AuthType
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.util.kotlin.Utils.Companion.toQuad
+import com.android.systemui.util.kotlin.Utils.Companion.toQuint
+import com.android.systemui.util.kotlin.Utils.Companion.toTriple
+import com.android.systemui.util.kotlin.sample
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.launch
+
+/** Sub-binder for [BiometricPromptLayout.iconView]. */
+object PromptIconViewBinder {
+ /**
+ * Binds [BiometricPromptLayout.iconView] and [BiometricPromptLayout.biometric_icon_overlay] to
+ * [PromptIconViewModel].
+ */
+ @JvmStatic
+ fun bind(
+ iconView: LottieAnimationView,
+ iconOverlayView: LottieAnimationView,
+ iconViewLayoutParamSizeOverride: Pair<Int, Int>?,
+ viewModel: PromptIconViewModel
+ ) {
+ iconView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.onConfigurationChanged(iconView.context.resources.configuration)
+ if (iconViewLayoutParamSizeOverride != null) {
+ iconView.layoutParams.width = iconViewLayoutParamSizeOverride.first
+ iconView.layoutParams.height = iconViewLayoutParamSizeOverride.second
+
+ iconOverlayView.layoutParams.width = iconViewLayoutParamSizeOverride.first
+ iconOverlayView.layoutParams.height = iconViewLayoutParamSizeOverride.second
+ }
+
+ var faceIcon: AnimatedVectorDrawable? = null
+ val faceIconCallback =
+ object : Animatable2.AnimationCallback() {
+ override fun onAnimationStart(drawable: Drawable) {
+ viewModel.onAnimationStart()
+ }
+
+ override fun onAnimationEnd(drawable: Drawable) {
+ viewModel.onAnimationEnd()
+ }
+ }
+
+ launch {
+ viewModel.activeAuthType.collect { activeAuthType ->
+ if (iconViewLayoutParamSizeOverride == null) {
+ val width: Int
+ val height: Int
+ when (activeAuthType) {
+ AuthType.Fingerprint,
+ AuthType.Coex -> {
+ width = viewModel.fingerprintIconWidth
+ height = viewModel.fingerprintIconHeight
+ }
+ AuthType.Face -> {
+ width = viewModel.faceIconWidth
+ height = viewModel.faceIconHeight
+ }
+ }
+
+ iconView.layoutParams.width = width
+ iconView.layoutParams.height = height
+
+ iconOverlayView.layoutParams.width = width
+ iconOverlayView.layoutParams.height = height
+ }
+ }
+ }
+
+ launch {
+ viewModel.iconAsset
+ .sample(
+ combine(
+ viewModel.activeAuthType,
+ viewModel.shouldAnimateIconView,
+ viewModel.shouldRepeatAnimation,
+ viewModel.showingError,
+ ::toQuad
+ ),
+ ::toQuint
+ )
+ .collect {
+ (
+ iconAsset,
+ activeAuthType,
+ shouldAnimateIconView,
+ shouldRepeatAnimation,
+ showingError) ->
+ if (iconAsset != -1) {
+ when (activeAuthType) {
+ AuthType.Fingerprint,
+ AuthType.Coex -> {
+ iconView.setAnimation(iconAsset)
+ iconView.frame = 0
+
+ if (shouldAnimateIconView) {
+ iconView.playAnimation()
+ }
+ }
+ AuthType.Face -> {
+ faceIcon?.apply {
+ unregisterAnimationCallback(faceIconCallback)
+ stop()
+ }
+ faceIcon =
+ iconView.context.getDrawable(iconAsset)
+ as AnimatedVectorDrawable
+ faceIcon?.apply {
+ iconView.setImageDrawable(this)
+ if (shouldAnimateIconView) {
+ forceAnimationOnUI()
+ if (shouldRepeatAnimation) {
+ registerAnimationCallback(faceIconCallback)
+ }
+ start()
+ }
+ }
+ }
+ }
+ LottieColorUtils.applyDynamicColors(iconView.context, iconView)
+ viewModel.setPreviousIconWasError(showingError)
+ }
+ }
+ }
+
+ launch {
+ viewModel.iconOverlayAsset
+ .sample(
+ combine(
+ viewModel.shouldAnimateIconOverlay,
+ viewModel.showingError,
+ ::Pair
+ ),
+ ::toTriple
+ )
+ .collect { (iconOverlayAsset, shouldAnimateIconOverlay, showingError) ->
+ if (iconOverlayAsset != -1) {
+ iconOverlayView.setAnimation(iconOverlayAsset)
+ iconOverlayView.frame = 0
+ LottieColorUtils.applyDynamicColors(
+ iconOverlayView.context,
+ iconOverlayView
+ )
+
+ if (shouldAnimateIconOverlay) {
+ iconOverlayView.playAnimation()
+ }
+ viewModel.setPreviousIconOverlayWasError(showingError)
+ }
+ }
+ }
+
+ launch {
+ viewModel.shouldFlipIconView.collect { shouldFlipIconView ->
+ if (shouldFlipIconView) {
+ iconView.rotation = 180f
+ }
+ }
+ }
+
+ launch {
+ viewModel.contentDescriptionId.collect { id ->
+ if (id != -1) {
+ iconView.contentDescription = iconView.context.getString(id)
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModel.kt
deleted file mode 100644
index dfd3a9b..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModel.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.systemui.biometrics.ui.viewmodel
-
-import android.annotation.RawRes
-import android.content.res.Configuration
-import com.android.systemui.res.R
-import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
-import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
-import com.android.systemui.biometrics.shared.model.DisplayRotation
-import com.android.systemui.biometrics.shared.model.FingerprintSensorType
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
-
-/** Models UI of [BiometricPromptLayout.iconView] */
-class PromptFingerprintIconViewModel
-@Inject
-constructor(
- private val displayStateInteractor: DisplayStateInteractor,
- promptSelectorInteractor: PromptSelectorInteractor,
-) {
- /** Current BiometricPromptLayout.iconView asset. */
- val iconAsset: Flow<Int> =
- combine(
- displayStateInteractor.currentRotation,
- displayStateInteractor.isFolded,
- displayStateInteractor.isInRearDisplayMode,
- promptSelectorInteractor.sensorType,
- ) {
- rotation: DisplayRotation,
- isFolded: Boolean,
- isInRearDisplayMode: Boolean,
- sensorType: FingerprintSensorType ->
- when (sensorType) {
- FingerprintSensorType.POWER_BUTTON ->
- getSideFpsAnimationAsset(rotation, isFolded, isInRearDisplayMode)
- // Replace below when non-SFPS iconAsset logic is migrated to this ViewModel
- else -> -1
- }
- }
-
- @RawRes
- private fun getSideFpsAnimationAsset(
- rotation: DisplayRotation,
- isDeviceFolded: Boolean,
- isInRearDisplayMode: Boolean,
- ): Int =
- when (rotation) {
- DisplayRotation.ROTATION_90 ->
- if (isInRearDisplayMode) {
- R.raw.biometricprompt_rear_portrait_reverse_base
- } else if (isDeviceFolded) {
- R.raw.biometricprompt_folded_base_topleft
- } else {
- R.raw.biometricprompt_portrait_base_topleft
- }
- DisplayRotation.ROTATION_270 ->
- if (isInRearDisplayMode) {
- R.raw.biometricprompt_rear_portrait_base
- } else if (isDeviceFolded) {
- R.raw.biometricprompt_folded_base_bottomright
- } else {
- R.raw.biometricprompt_portrait_base_bottomright
- }
- else ->
- if (isInRearDisplayMode) {
- R.raw.biometricprompt_rear_landscape_base
- } else if (isDeviceFolded) {
- R.raw.biometricprompt_folded_base_default
- } else {
- R.raw.biometricprompt_landscape_base
- }
- }
-
- /** Called on configuration changes */
- fun onConfigurationChanged(newConfig: Configuration) {
- displayStateInteractor.onConfigurationChanged(newConfig)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
new file mode 100644
index 0000000..11a5d8b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
@@ -0,0 +1,721 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.biometrics.ui.viewmodel
+
+import android.annotation.DrawableRes
+import android.annotation.RawRes
+import android.content.res.Configuration
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
+import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
+import com.android.systemui.biometrics.shared.model.DisplayRotation
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.res.R
+import com.android.systemui.util.kotlin.combine
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+/**
+ * Models UI of [BiometricPromptLayout.iconView] and [BiometricPromptLayout.biometric_icon_overlay]
+ */
+class PromptIconViewModel
+constructor(
+ promptViewModel: PromptViewModel,
+ private val displayStateInteractor: DisplayStateInteractor,
+ promptSelectorInteractor: PromptSelectorInteractor
+) {
+
+ /** Auth types for the UI to display. */
+ enum class AuthType {
+ Fingerprint,
+ Face,
+ Coex
+ }
+
+ /**
+ * Indicates what auth type the UI currently displays.
+ * Fingerprint-only auth -> Fingerprint
+ * Face-only auth -> Face
+ * Co-ex auth, implicit flow -> Face
+ * Co-ex auth, explicit flow -> Coex
+ */
+ val activeAuthType: Flow<AuthType> =
+ combine(
+ promptViewModel.modalities.distinctUntilChanged(),
+ promptViewModel.faceMode.distinctUntilChanged()
+ ) { modalities, faceMode ->
+ if (modalities.hasFaceAndFingerprint && !faceMode) {
+ AuthType.Coex
+ } else if (modalities.hasFaceOnly || faceMode) {
+ AuthType.Face
+ } else if (modalities.hasFingerprintOnly) {
+ AuthType.Fingerprint
+ } else {
+ throw IllegalStateException("unexpected modality: $modalities")
+ }
+ }
+
+ /** Whether an error message is currently being shown. */
+ val showingError = promptViewModel.showingError
+
+ /** Whether the previous icon shown displayed an error. */
+ private val _previousIconWasError: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
+ /** Whether the previous icon overlay shown displayed an error. */
+ private val _previousIconOverlayWasError: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
+ fun setPreviousIconWasError(previousIconWasError: Boolean) {
+ _previousIconWasError.value = previousIconWasError
+ }
+
+ fun setPreviousIconOverlayWasError(previousIconOverlayWasError: Boolean) {
+ _previousIconOverlayWasError.value = previousIconOverlayWasError
+ }
+
+ /** Called when iconView begins animating. */
+ fun onAnimationStart() {
+ _animationEnded.value = false
+ }
+
+ /** Called when iconView ends animating. */
+ fun onAnimationEnd() {
+ _animationEnded.value = true
+ }
+
+ private val _animationEnded: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
+ /**
+ * Whether a face iconView should pulse (i.e. while isAuthenticating and previous animation
+ * ended).
+ */
+ val shouldPulseAnimation: Flow<Boolean> =
+ combine(_animationEnded, promptViewModel.isAuthenticating) {
+ animationEnded,
+ isAuthenticating ->
+ animationEnded && isAuthenticating
+ }
+ .distinctUntilChanged()
+
+ private val _lastPulseLightToDark: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
+ /** Tracks whether a face iconView last pulsed light to dark (vs. dark to light) */
+ val lastPulseLightToDark: Flow<Boolean> = _lastPulseLightToDark.asStateFlow()
+
+ /** Layout params for fingerprint iconView */
+ val fingerprintIconWidth: Int = promptViewModel.fingerprintIconWidth
+ val fingerprintIconHeight: Int = promptViewModel.fingerprintIconHeight
+
+ /** Layout params for face iconView */
+ val faceIconWidth: Int = promptViewModel.faceIconWidth
+ val faceIconHeight: Int = promptViewModel.faceIconHeight
+
+ /** Current BiometricPromptLayout.iconView asset. */
+ val iconAsset: Flow<Int> =
+ activeAuthType.flatMapLatest { activeAuthType: AuthType ->
+ when (activeAuthType) {
+ AuthType.Fingerprint ->
+ combine(
+ displayStateInteractor.currentRotation,
+ displayStateInteractor.isFolded,
+ displayStateInteractor.isInRearDisplayMode,
+ promptSelectorInteractor.sensorType,
+ promptViewModel.isAuthenticated,
+ promptViewModel.isAuthenticating,
+ promptViewModel.showingError
+ ) {
+ rotation: DisplayRotation,
+ isFolded: Boolean,
+ isInRearDisplayMode: Boolean,
+ sensorType: FingerprintSensorType,
+ authState: PromptAuthState,
+ isAuthenticating: Boolean,
+ showingError: Boolean ->
+ when (sensorType) {
+ FingerprintSensorType.POWER_BUTTON ->
+ getSfpsIconViewAsset(rotation, isFolded, isInRearDisplayMode)
+ else ->
+ getFingerprintIconViewAsset(
+ authState.isAuthenticated,
+ isAuthenticating,
+ showingError
+ )
+ }
+ }
+ AuthType.Face ->
+ shouldPulseAnimation.flatMapLatest { shouldPulseAnimation: Boolean ->
+ if (shouldPulseAnimation) {
+ val iconAsset =
+ if (_lastPulseLightToDark.value) {
+ R.drawable.face_dialog_pulse_dark_to_light
+ } else {
+ R.drawable.face_dialog_pulse_light_to_dark
+ }
+ _lastPulseLightToDark.value = !_lastPulseLightToDark.value
+ flowOf(iconAsset)
+ } else {
+ combine(
+ promptViewModel.isAuthenticated.distinctUntilChanged(),
+ promptViewModel.isAuthenticating.distinctUntilChanged(),
+ promptViewModel.isPendingConfirmation.distinctUntilChanged(),
+ promptViewModel.showingError.distinctUntilChanged()
+ ) {
+ authState: PromptAuthState,
+ isAuthenticating: Boolean,
+ isPendingConfirmation: Boolean,
+ showingError: Boolean ->
+ getFaceIconViewAsset(
+ authState,
+ isAuthenticating,
+ isPendingConfirmation,
+ showingError
+ )
+ }
+ }
+ }
+ AuthType.Coex ->
+ combine(
+ displayStateInteractor.currentRotation,
+ displayStateInteractor.isFolded,
+ displayStateInteractor.isInRearDisplayMode,
+ promptSelectorInteractor.sensorType,
+ promptViewModel.isAuthenticated,
+ promptViewModel.isAuthenticating,
+ promptViewModel.isPendingConfirmation,
+ promptViewModel.showingError,
+ ) {
+ rotation: DisplayRotation,
+ isFolded: Boolean,
+ isInRearDisplayMode: Boolean,
+ sensorType: FingerprintSensorType,
+ authState: PromptAuthState,
+ isAuthenticating: Boolean,
+ isPendingConfirmation: Boolean,
+ showingError: Boolean ->
+ when (sensorType) {
+ FingerprintSensorType.POWER_BUTTON ->
+ getSfpsIconViewAsset(rotation, isFolded, isInRearDisplayMode)
+ else ->
+ getCoexIconViewAsset(
+ authState,
+ isAuthenticating,
+ isPendingConfirmation,
+ showingError
+ )
+ }
+ }
+ }
+ }
+
+ private fun getFingerprintIconViewAsset(
+ isAuthenticated: Boolean,
+ isAuthenticating: Boolean,
+ showingError: Boolean
+ ): Int =
+ if (isAuthenticated) {
+ if (_previousIconWasError.value) {
+ R.raw.fingerprint_dialogue_error_to_success_lottie
+ } else {
+ R.raw.fingerprint_dialogue_fingerprint_to_success_lottie
+ }
+ } else if (isAuthenticating) {
+ if (_previousIconWasError.value) {
+ R.raw.fingerprint_dialogue_error_to_fingerprint_lottie
+ } else {
+ R.raw.fingerprint_dialogue_fingerprint_to_error_lottie
+ }
+ } else if (showingError) {
+ R.raw.fingerprint_dialogue_fingerprint_to_error_lottie
+ } else {
+ -1
+ }
+
+ @RawRes
+ private fun getSfpsIconViewAsset(
+ rotation: DisplayRotation,
+ isDeviceFolded: Boolean,
+ isInRearDisplayMode: Boolean,
+ ): Int =
+ when (rotation) {
+ DisplayRotation.ROTATION_90 ->
+ if (isInRearDisplayMode) {
+ R.raw.biometricprompt_rear_portrait_reverse_base
+ } else if (isDeviceFolded) {
+ R.raw.biometricprompt_folded_base_topleft
+ } else {
+ R.raw.biometricprompt_portrait_base_topleft
+ }
+ DisplayRotation.ROTATION_270 ->
+ if (isInRearDisplayMode) {
+ R.raw.biometricprompt_rear_portrait_base
+ } else if (isDeviceFolded) {
+ R.raw.biometricprompt_folded_base_bottomright
+ } else {
+ R.raw.biometricprompt_portrait_base_bottomright
+ }
+ else ->
+ if (isInRearDisplayMode) {
+ R.raw.biometricprompt_rear_landscape_base
+ } else if (isDeviceFolded) {
+ R.raw.biometricprompt_folded_base_default
+ } else {
+ R.raw.biometricprompt_landscape_base
+ }
+ }
+
+ @DrawableRes
+ private fun getFaceIconViewAsset(
+ authState: PromptAuthState,
+ isAuthenticating: Boolean,
+ isPendingConfirmation: Boolean,
+ showingError: Boolean
+ ): Int =
+ if (authState.isAuthenticated && isPendingConfirmation) {
+ R.drawable.face_dialog_wink_from_dark
+ } else if (authState.isAuthenticated) {
+ R.drawable.face_dialog_dark_to_checkmark
+ } else if (isAuthenticating) {
+ _lastPulseLightToDark.value = false
+ R.drawable.face_dialog_pulse_dark_to_light
+ } else if (showingError) {
+ R.drawable.face_dialog_dark_to_error
+ } else if (_previousIconWasError.value) {
+ R.drawable.face_dialog_error_to_idle
+ } else {
+ R.drawable.face_dialog_idle_static
+ }
+
+ @RawRes
+ private fun getCoexIconViewAsset(
+ authState: PromptAuthState,
+ isAuthenticating: Boolean,
+ isPendingConfirmation: Boolean,
+ showingError: Boolean
+ ): Int =
+ if (authState.isAuthenticatedAndExplicitlyConfirmed) {
+ R.raw.fingerprint_dialogue_unlocked_to_checkmark_success_lottie
+ } else if (isPendingConfirmation) {
+ if (_previousIconWasError.value) {
+ R.raw.fingerprint_dialogue_error_to_unlock_lottie
+ } else {
+ R.raw.fingerprint_dialogue_fingerprint_to_unlock_lottie
+ }
+ } else if (authState.isAuthenticated) {
+ if (_previousIconWasError.value) {
+ R.raw.fingerprint_dialogue_error_to_success_lottie
+ } else {
+ R.raw.fingerprint_dialogue_fingerprint_to_success_lottie
+ }
+ } else if (isAuthenticating) {
+ if (_previousIconWasError.value) {
+ R.raw.fingerprint_dialogue_error_to_fingerprint_lottie
+ } else {
+ R.raw.fingerprint_dialogue_fingerprint_to_error_lottie
+ }
+ } else if (showingError) {
+ R.raw.fingerprint_dialogue_fingerprint_to_error_lottie
+ } else {
+ -1
+ }
+
+ /** Current BiometricPromptLayout.biometric_icon_overlay asset. */
+ var iconOverlayAsset: Flow<Int> =
+ activeAuthType.flatMapLatest { activeAuthType: AuthType ->
+ when (activeAuthType) {
+ AuthType.Fingerprint,
+ AuthType.Coex ->
+ combine(
+ displayStateInteractor.currentRotation,
+ promptSelectorInteractor.sensorType,
+ promptViewModel.isAuthenticated,
+ promptViewModel.isAuthenticating,
+ promptViewModel.showingError
+ ) {
+ rotation: DisplayRotation,
+ sensorType: FingerprintSensorType,
+ authState: PromptAuthState,
+ isAuthenticating: Boolean,
+ showingError: Boolean ->
+ when (sensorType) {
+ FingerprintSensorType.POWER_BUTTON ->
+ getSfpsIconOverlayAsset(
+ rotation,
+ authState.isAuthenticated,
+ isAuthenticating,
+ showingError
+ )
+ else -> -1
+ }
+ }
+ AuthType.Face -> flowOf(-1)
+ }
+ }
+
+ @RawRes
+ private fun getSfpsIconOverlayAsset(
+ rotation: DisplayRotation,
+ isAuthenticated: Boolean,
+ isAuthenticating: Boolean,
+ showingError: Boolean
+ ): Int =
+ if (isAuthenticated) {
+ if (_previousIconOverlayWasError.value) {
+ when (rotation) {
+ DisplayRotation.ROTATION_0 ->
+ R.raw.biometricprompt_symbol_error_to_success_landscape
+ DisplayRotation.ROTATION_90 ->
+ R.raw.biometricprompt_symbol_error_to_success_portrait_topleft
+ DisplayRotation.ROTATION_180 ->
+ R.raw.biometricprompt_symbol_error_to_success_landscape
+ DisplayRotation.ROTATION_270 ->
+ R.raw.biometricprompt_symbol_error_to_success_portrait_bottomright
+ }
+ } else {
+ when (rotation) {
+ DisplayRotation.ROTATION_0 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
+ DisplayRotation.ROTATION_90 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_topleft
+ DisplayRotation.ROTATION_180 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
+ DisplayRotation.ROTATION_270 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_bottomright
+ }
+ }
+ } else if (isAuthenticating) {
+ if (_previousIconOverlayWasError.value) {
+ when (rotation) {
+ DisplayRotation.ROTATION_0 ->
+ R.raw.biometricprompt_symbol_error_to_fingerprint_landscape
+ DisplayRotation.ROTATION_90 ->
+ R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_topleft
+ DisplayRotation.ROTATION_180 ->
+ R.raw.biometricprompt_symbol_error_to_fingerprint_landscape
+ DisplayRotation.ROTATION_270 ->
+ R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_bottomright
+ }
+ } else {
+ when (rotation) {
+ DisplayRotation.ROTATION_0 ->
+ R.raw.biometricprompt_fingerprint_to_error_landscape
+ DisplayRotation.ROTATION_90 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft
+ DisplayRotation.ROTATION_180 ->
+ R.raw.biometricprompt_fingerprint_to_error_landscape
+ DisplayRotation.ROTATION_270 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright
+ }
+ }
+ } else if (showingError) {
+ when (rotation) {
+ DisplayRotation.ROTATION_0 -> R.raw.biometricprompt_fingerprint_to_error_landscape
+ DisplayRotation.ROTATION_90 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft
+ DisplayRotation.ROTATION_180 -> R.raw.biometricprompt_fingerprint_to_error_landscape
+ DisplayRotation.ROTATION_270 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright
+ }
+ } else {
+ -1
+ }
+
+ /** Content description for iconView */
+ val contentDescriptionId: Flow<Int> =
+ activeAuthType.flatMapLatest { activeAuthType: AuthType ->
+ when (activeAuthType) {
+ AuthType.Fingerprint,
+ AuthType.Coex ->
+ combine(
+ promptSelectorInteractor.sensorType,
+ promptViewModel.isAuthenticated,
+ promptViewModel.isAuthenticating,
+ promptViewModel.isPendingConfirmation,
+ promptViewModel.showingError
+ ) {
+ sensorType: FingerprintSensorType,
+ authState: PromptAuthState,
+ isAuthenticating: Boolean,
+ isPendingConfirmation: Boolean,
+ showingError: Boolean ->
+ getFingerprintIconContentDescriptionId(
+ sensorType,
+ authState.isAuthenticated,
+ isAuthenticating,
+ isPendingConfirmation,
+ showingError
+ )
+ }
+ AuthType.Face ->
+ combine(
+ promptViewModel.isAuthenticated,
+ promptViewModel.isAuthenticating,
+ promptViewModel.showingError,
+ ) { authState: PromptAuthState, isAuthenticating: Boolean, showingError: Boolean
+ ->
+ getFaceIconContentDescriptionId(authState, isAuthenticating, showingError)
+ }
+ }
+ }
+
+ private fun getFingerprintIconContentDescriptionId(
+ sensorType: FingerprintSensorType,
+ isAuthenticated: Boolean,
+ isAuthenticating: Boolean,
+ isPendingConfirmation: Boolean,
+ showingError: Boolean
+ ): Int =
+ if (isPendingConfirmation) {
+ when (sensorType) {
+ FingerprintSensorType.POWER_BUTTON ->
+ R.string.security_settings_sfps_enroll_find_sensor_message
+ else -> R.string.fingerprint_dialog_authenticated_confirmation
+ }
+ } else if (isAuthenticating || isAuthenticated) {
+ when (sensorType) {
+ FingerprintSensorType.POWER_BUTTON ->
+ R.string.security_settings_sfps_enroll_find_sensor_message
+ else -> R.string.fingerprint_dialog_touch_sensor
+ }
+ } else if (showingError) {
+ R.string.biometric_dialog_try_again
+ } else {
+ -1
+ }
+
+ private fun getFaceIconContentDescriptionId(
+ authState: PromptAuthState,
+ isAuthenticating: Boolean,
+ showingError: Boolean
+ ): Int =
+ if (authState.isAuthenticatedAndExplicitlyConfirmed) {
+ R.string.biometric_dialog_face_icon_description_confirmed
+ } else if (authState.isAuthenticated) {
+ R.string.biometric_dialog_face_icon_description_authenticated
+ } else if (isAuthenticating) {
+ R.string.biometric_dialog_face_icon_description_authenticating
+ } else if (showingError) {
+ R.string.keyguard_face_failed
+ } else {
+ R.string.biometric_dialog_face_icon_description_idle
+ }
+
+ /** Whether the current BiometricPromptLayout.iconView asset animation should be playing. */
+ val shouldAnimateIconView: Flow<Boolean> =
+ activeAuthType.flatMapLatest { activeAuthType: AuthType ->
+ when (activeAuthType) {
+ AuthType.Fingerprint ->
+ combine(
+ promptSelectorInteractor.sensorType,
+ promptViewModel.isAuthenticated,
+ promptViewModel.isAuthenticating,
+ promptViewModel.showingError
+ ) {
+ sensorType: FingerprintSensorType,
+ authState: PromptAuthState,
+ isAuthenticating: Boolean,
+ showingError: Boolean ->
+ when (sensorType) {
+ FingerprintSensorType.POWER_BUTTON ->
+ shouldAnimateSfpsIconView(
+ authState.isAuthenticated,
+ isAuthenticating,
+ showingError
+ )
+ else ->
+ shouldAnimateFingerprintIconView(
+ authState.isAuthenticated,
+ isAuthenticating,
+ showingError
+ )
+ }
+ }
+ AuthType.Face ->
+ combine(
+ promptViewModel.isAuthenticated,
+ promptViewModel.isAuthenticating,
+ promptViewModel.showingError
+ ) { authState: PromptAuthState, isAuthenticating: Boolean, showingError: Boolean
+ ->
+ isAuthenticating ||
+ authState.isAuthenticated ||
+ showingError ||
+ _previousIconWasError.value
+ }
+ AuthType.Coex ->
+ combine(
+ promptSelectorInteractor.sensorType,
+ promptViewModel.isAuthenticated,
+ promptViewModel.isAuthenticating,
+ promptViewModel.isPendingConfirmation,
+ promptViewModel.showingError,
+ ) {
+ sensorType: FingerprintSensorType,
+ authState: PromptAuthState,
+ isAuthenticating: Boolean,
+ isPendingConfirmation: Boolean,
+ showingError: Boolean ->
+ when (sensorType) {
+ FingerprintSensorType.POWER_BUTTON ->
+ shouldAnimateSfpsIconView(
+ authState.isAuthenticated,
+ isAuthenticating,
+ showingError
+ )
+ else ->
+ shouldAnimateCoexIconView(
+ authState.isAuthenticated,
+ isAuthenticating,
+ isPendingConfirmation,
+ showingError
+ )
+ }
+ }
+ }
+ }
+
+ private fun shouldAnimateFingerprintIconView(
+ isAuthenticated: Boolean,
+ isAuthenticating: Boolean,
+ showingError: Boolean
+ ) = (isAuthenticating && _previousIconWasError.value) || isAuthenticated || showingError
+
+ private fun shouldAnimateSfpsIconView(
+ isAuthenticated: Boolean,
+ isAuthenticating: Boolean,
+ showingError: Boolean
+ ) = isAuthenticated || isAuthenticating || showingError
+
+ private fun shouldAnimateCoexIconView(
+ isAuthenticated: Boolean,
+ isAuthenticating: Boolean,
+ isPendingConfirmation: Boolean,
+ showingError: Boolean
+ ) =
+ (isAuthenticating && _previousIconWasError.value) ||
+ isPendingConfirmation ||
+ isAuthenticated ||
+ showingError
+
+ /** Whether the current iconOverlayAsset animation should be playing. */
+ val shouldAnimateIconOverlay: Flow<Boolean> =
+ activeAuthType.flatMapLatest { activeAuthType: AuthType ->
+ when (activeAuthType) {
+ AuthType.Fingerprint,
+ AuthType.Coex ->
+ combine(
+ promptSelectorInteractor.sensorType,
+ promptViewModel.isAuthenticated,
+ promptViewModel.isAuthenticating,
+ promptViewModel.showingError
+ ) {
+ sensorType: FingerprintSensorType,
+ authState: PromptAuthState,
+ isAuthenticating: Boolean,
+ showingError: Boolean ->
+ when (sensorType) {
+ FingerprintSensorType.POWER_BUTTON ->
+ shouldAnimateSfpsIconOverlay(
+ authState.isAuthenticated,
+ isAuthenticating,
+ showingError
+ )
+ else -> false
+ }
+ }
+ AuthType.Face -> flowOf(false)
+ }
+ }
+
+ private fun shouldAnimateSfpsIconOverlay(
+ isAuthenticated: Boolean,
+ isAuthenticating: Boolean,
+ showingError: Boolean
+ ) = (isAuthenticating && _previousIconOverlayWasError.value) || isAuthenticated || showingError
+
+ /** Whether the iconView should be flipped due to a device using reverse default rotation . */
+ val shouldFlipIconView: Flow<Boolean> =
+ activeAuthType.flatMapLatest { activeAuthType: AuthType ->
+ when (activeAuthType) {
+ AuthType.Fingerprint,
+ AuthType.Coex ->
+ combine(
+ promptSelectorInteractor.sensorType,
+ displayStateInteractor.currentRotation
+ ) { sensorType: FingerprintSensorType, rotation: DisplayRotation ->
+ when (sensorType) {
+ FingerprintSensorType.POWER_BUTTON ->
+ (rotation == DisplayRotation.ROTATION_180)
+ else -> false
+ }
+ }
+ AuthType.Face -> flowOf(false)
+ }
+ }
+
+ /** Whether the current BiometricPromptLayout.iconView asset animation should be repeated. */
+ val shouldRepeatAnimation: Flow<Boolean> =
+ activeAuthType.flatMapLatest { activeAuthType: AuthType ->
+ when (activeAuthType) {
+ AuthType.Fingerprint,
+ AuthType.Coex -> flowOf(false)
+ AuthType.Face -> promptViewModel.isAuthenticating.map { it }
+ }
+ }
+
+ /** Called on configuration changes */
+ fun onConfigurationChanged(newConfig: Configuration) {
+ displayStateInteractor.onConfigurationChanged(newConfig)
+ }
+
+ /** iconView assets for caching */
+ fun getRawAssets(hasSfps: Boolean): List<Int> {
+ return if (hasSfps) {
+ listOf(
+ R.raw.biometricprompt_fingerprint_to_error_landscape,
+ R.raw.biometricprompt_folded_base_bottomright,
+ R.raw.biometricprompt_folded_base_default,
+ R.raw.biometricprompt_folded_base_topleft,
+ R.raw.biometricprompt_landscape_base,
+ R.raw.biometricprompt_portrait_base_bottomright,
+ R.raw.biometricprompt_portrait_base_topleft,
+ R.raw.biometricprompt_symbol_error_to_fingerprint_landscape,
+ R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_bottomright,
+ R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_topleft,
+ R.raw.biometricprompt_symbol_error_to_success_landscape,
+ R.raw.biometricprompt_symbol_error_to_success_portrait_bottomright,
+ R.raw.biometricprompt_symbol_error_to_success_portrait_topleft,
+ R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright,
+ R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft,
+ R.raw.biometricprompt_symbol_fingerprint_to_success_landscape,
+ R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_bottomright,
+ R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_topleft
+ )
+ } else {
+ listOf(
+ R.raw.fingerprint_dialogue_error_to_fingerprint_lottie,
+ R.raw.fingerprint_dialogue_error_to_success_lottie,
+ R.raw.fingerprint_dialogue_fingerprint_to_error_lottie,
+ R.raw.fingerprint_dialogue_fingerprint_to_success_lottie
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 267afae..e49b4a7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -28,10 +28,10 @@
import com.android.systemui.biometrics.shared.model.BiometricModality
import com.android.systemui.biometrics.shared.model.DisplayRotation
import com.android.systemui.biometrics.shared.model.PromptKind
-import com.android.systemui.biometrics.ui.binder.Spaghetti
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
+import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
import kotlinx.coroutines.Job
@@ -39,7 +39,6 @@
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -51,25 +50,29 @@
class PromptViewModel
@Inject
constructor(
- private val displayStateInteractor: DisplayStateInteractor,
- private val promptSelectorInteractor: PromptSelectorInteractor,
+ displayStateInteractor: DisplayStateInteractor,
+ promptSelectorInteractor: PromptSelectorInteractor,
private val vibrator: VibratorHelper,
@Application context: Context,
private val featureFlags: FeatureFlags,
) {
- /** Models UI of [BiometricPromptLayout.iconView] */
- val fingerprintIconViewModel: PromptFingerprintIconViewModel =
- PromptFingerprintIconViewModel(displayStateInteractor, promptSelectorInteractor)
-
/** The set of modalities available for this prompt */
val modalities: Flow<BiometricModalities> =
promptSelectorInteractor.prompt
.map { it?.modalities ?: BiometricModalities() }
.distinctUntilChanged()
- // TODO(b/251476085): remove after icon controllers are migrated - do not keep this state
- private var _legacyState = MutableStateFlow(Spaghetti.BiometricState.STATE_IDLE)
- val legacyState: StateFlow<Spaghetti.BiometricState> = _legacyState.asStateFlow()
+ /** Layout params for fingerprint iconView */
+ val fingerprintIconWidth: Int =
+ context.resources.getDimensionPixelSize(R.dimen.biometric_dialog_fingerprint_icon_width)
+ val fingerprintIconHeight: Int =
+ context.resources.getDimensionPixelSize(R.dimen.biometric_dialog_fingerprint_icon_height)
+
+ /** Layout params for face iconView */
+ val faceIconWidth: Int =
+ context.resources.getDimensionPixelSize(R.dimen.biometric_dialog_face_icon_size)
+ val faceIconHeight: Int =
+ context.resources.getDimensionPixelSize(R.dimen.biometric_dialog_face_icon_size)
private val _isAuthenticating: MutableStateFlow<Boolean> = MutableStateFlow(false)
@@ -82,6 +85,12 @@
/** If the user has successfully authenticated and confirmed (when explicitly required). */
val isAuthenticated: Flow<PromptAuthState> = _isAuthenticated.asStateFlow()
+ /** If the auth is pending confirmation. */
+ val isPendingConfirmation: Flow<Boolean> =
+ isAuthenticated.map { authState ->
+ authState.isAuthenticated && authState.needsUserConfirmation
+ }
+
private val _isOverlayTouched: MutableStateFlow<Boolean> = MutableStateFlow(false)
/** The kind of credential the user has. */
@@ -96,6 +105,9 @@
/** A message to show the user, if there is an error, hint, or help to show. */
val message: Flow<PromptMessage> = _message.asStateFlow()
+ /** Whether an error message is currently being shown. */
+ val showingError: Flow<Boolean> = message.map { it.isError }.distinctUntilChanged()
+
private val isRetrySupported: Flow<Boolean> = modalities.map { it.hasFace }
private val _fingerprintStartMode = MutableStateFlow(FingerprintStartMode.Pending)
@@ -141,6 +153,38 @@
!isOverlayTouched && size.isNotSmall
}
+ /**
+ * When fingerprint and face modalities are enrolled, indicates whether only face auth has
+ * started.
+ *
+ * True when fingerprint and face modalities are enrolled and implicit flow is active. This
+ * occurs in co-ex auth when confirmation is not required and only face auth is started, then
+ * becomes false when device transitions to explicit flow after a first error, when the
+ * fingerprint sensor is started.
+ *
+ * False when the dialog opens in explicit flow (fingerprint and face modalities enrolled but
+ * confirmation is required), or if user has only fingerprint enrolled, or only face enrolled.
+ */
+ val faceMode: Flow<Boolean> =
+ combine(modalities, isConfirmationRequired, fingerprintStartMode) {
+ modalities: BiometricModalities,
+ isConfirmationRequired: Boolean,
+ fingerprintStartMode: FingerprintStartMode ->
+ if (modalities.hasFaceAndFingerprint) {
+ if (isConfirmationRequired) {
+ false
+ } else {
+ !fingerprintStartMode.isStarted
+ }
+ } else {
+ false
+ }
+ }
+ .distinctUntilChanged()
+
+ val iconViewModel: PromptIconViewModel =
+ PromptIconViewModel(this, displayStateInteractor, promptSelectorInteractor)
+
/** Padding for prompt UI elements */
val promptPadding: Flow<Rect> =
combine(size, displayStateInteractor.currentRotation) { size, rotation ->
@@ -184,9 +228,9 @@
val isConfirmButtonVisible: Flow<Boolean> =
combine(
size,
- isAuthenticated,
- ) { size, authState ->
- size.isNotSmall && authState.isAuthenticated && authState.needsUserConfirmation
+ isPendingConfirmation,
+ ) { size, isPendingConfirmation ->
+ size.isNotSmall && isPendingConfirmation
}
.distinctUntilChanged()
@@ -293,7 +337,6 @@
_isAuthenticated.value = PromptAuthState(false)
_forceMediumSize.value = true
_message.value = PromptMessage.Error(message)
- _legacyState.value = Spaghetti.BiometricState.STATE_ERROR
if (hapticFeedback) {
vibrator.error(failedModality)
@@ -305,7 +348,7 @@
if (authenticateAfterError) {
showAuthenticating(messageAfterError)
} else {
- showInfo(messageAfterError)
+ showHelp(messageAfterError)
}
}
}
@@ -325,15 +368,12 @@
private fun supportsRetry(failedModality: BiometricModality) =
failedModality == BiometricModality.Face
- suspend fun showHelp(message: String) = showHelp(message, clearIconError = false)
- suspend fun showInfo(message: String) = showHelp(message, clearIconError = true)
-
/**
* Show a persistent help message.
*
* Will be show even if the user has already authenticated.
*/
- private suspend fun showHelp(message: String, clearIconError: Boolean) {
+ suspend fun showHelp(message: String) {
val alreadyAuthenticated = _isAuthenticated.value.isAuthenticated
if (!alreadyAuthenticated) {
_isAuthenticating.value = false
@@ -343,16 +383,6 @@
_message.value =
if (message.isNotBlank()) PromptMessage.Help(message) else PromptMessage.Empty
_forceMediumSize.value = true
- _legacyState.value =
- if (alreadyAuthenticated && isConfirmationRequired.first()) {
- Spaghetti.BiometricState.STATE_PENDING_CONFIRMATION
- } else if (alreadyAuthenticated && !isConfirmationRequired.first()) {
- Spaghetti.BiometricState.STATE_AUTHENTICATED
- } else if (clearIconError) {
- Spaghetti.BiometricState.STATE_IDLE
- } else {
- Spaghetti.BiometricState.STATE_HELP
- }
messageJob?.cancel()
messageJob = null
@@ -376,7 +406,6 @@
_message.value =
if (message.isNotBlank()) PromptMessage.Help(message) else PromptMessage.Empty
_forceMediumSize.value = true
- _legacyState.value = Spaghetti.BiometricState.STATE_HELP
messageJob?.cancel()
messageJob = launch {
@@ -396,7 +425,6 @@
_isAuthenticating.value = true
_isAuthenticated.value = PromptAuthState(false)
_message.value = if (message.isBlank()) PromptMessage.Empty else PromptMessage.Help(message)
- _legacyState.value = Spaghetti.BiometricState.STATE_AUTHENTICATING
// reset the try again button(s) after the user attempts a retry
if (isRetry) {
@@ -427,12 +455,6 @@
_isAuthenticated.value =
PromptAuthState(true, modality, needsUserConfirmation, dismissAfterDelay)
_message.value = PromptMessage.Empty
- _legacyState.value =
- if (needsUserConfirmation) {
- Spaghetti.BiometricState.STATE_PENDING_CONFIRMATION
- } else {
- Spaghetti.BiometricState.STATE_AUTHENTICATED
- }
if (!needsUserConfirmation) {
vibrator.success(modality)
@@ -472,7 +494,6 @@
_isAuthenticated.value = authState.asExplicitlyConfirmed()
_message.value = PromptMessage.Empty
- _legacyState.value = Spaghetti.BiometricState.STATE_AUTHENTICATED
vibrator.success(authState.authenticatedModality)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index 6e26fe9..21578f4 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
@@ -28,7 +28,6 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.DejankUtils
-import com.android.systemui.res.R
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN
@@ -41,8 +40,10 @@
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.TrustRepository
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -73,6 +74,7 @@
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val trustRepository: TrustRepository,
@Application private val applicationScope: CoroutineScope,
+ private val selectedUserInteractor: SelectedUserInteractor,
) {
private val passiveAuthBouncerDelay =
context.resources.getInteger(R.integer.primary_bouncer_passive_auth_delay).toLong()
@@ -144,6 +146,16 @@
/** Show the bouncer if necessary and set the relevant states. */
@JvmOverloads
fun show(isScrimmed: Boolean) {
+ if (primaryBouncerView.delegate == null) {
+ Log.d(
+ TAG,
+ "PrimaryBouncerInteractor#show is being called before the " +
+ "primaryBouncerDelegate is set. Let's exit early so we don't set the wrong " +
+ "primaryBouncer state."
+ )
+ return
+ }
+
// Reset some states as we show the bouncer.
repository.setKeyguardAuthenticatedBiometrics(null)
repository.setPrimaryStartingToHide(false)
@@ -384,7 +396,7 @@
/** Returns whether the bouncer should be full screen. */
private fun needsFullscreenBouncer(): Boolean {
val mode: KeyguardSecurityModel.SecurityMode =
- keyguardSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser())
+ keyguardSecurityModel.getSecurityMode(selectedUserInteractor.getSelectedUserId())
return mode == KeyguardSecurityModel.SecurityMode.SimPin ||
mode == KeyguardSecurityModel.SecurityMode.SimPuk
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
index 36e5db4..ac3d4b6 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
@@ -26,7 +26,6 @@
import com.android.keyguard.KeyguardSecurityContainerController
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardSecurityView
-import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE
@@ -37,6 +36,7 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.log.BouncerLogger
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
@@ -53,6 +53,7 @@
bouncerMessageInteractor: BouncerMessageInteractor,
bouncerLogger: BouncerLogger,
featureFlags: FeatureFlags,
+ selectedUserInteractor: SelectedUserInteractor,
) {
// Builds the KeyguardSecurityContainerController from bouncer view group.
val securityContainerController: KeyguardSecurityContainerController =
@@ -84,7 +85,7 @@
override fun showNextSecurityScreenOrFinish(): Boolean {
return securityContainerController.dismiss(
- KeyguardUpdateMonitor.getCurrentUser()
+ selectedUserInteractor.getSelectedUserId()
)
}
@@ -220,7 +221,7 @@
launch {
viewModel.keyguardAuthenticated.collect {
securityContainerController.finish(
- KeyguardUpdateMonitor.getCurrentUser()
+ selectedUserInteractor.getSelectedUserId()
)
viewModel.notifyKeyguardAuthenticated()
}
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
index b268095..11c7a31 100644
--- a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
@@ -27,17 +27,16 @@
import android.os.RemoteException
import android.util.Log
import android.view.WindowManager
-import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.ActivityIntentHelper
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.system.ActivityManagerKt.isInForeground
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -59,7 +58,7 @@
private val cameraIntents: CameraIntentsWrapper,
private val contentResolver: ContentResolver,
@Main private val uiExecutor: Executor,
- private val userTracker: UserTracker
+ private val selectedUserInteractor: SelectedUserInteractor,
) {
/**
* Whether the camera application can be launched for the camera launch gesture.
@@ -72,12 +71,12 @@
val resolveInfo: ResolveInfo? = packageManager.resolveActivityAsUser(
getStartCameraIntent(),
PackageManager.MATCH_DEFAULT_ONLY,
- KeyguardUpdateMonitor.getCurrentUser()
+ selectedUserInteractor.getSelectedUserId()
)
val resolvedPackage = resolveInfo?.activityInfo?.packageName
return (resolvedPackage != null &&
(statusBarState != StatusBarState.SHADE ||
- !activityManager.isInForeground(resolvedPackage)))
+ !activityManager.isInForeground(resolvedPackage)))
}
/**
@@ -89,7 +88,7 @@
val intent: Intent = getStartCameraIntent()
intent.putExtra(CameraIntents.EXTRA_LAUNCH_SOURCE, source)
val wouldLaunchResolverActivity = activityIntentHelper.wouldLaunchResolverActivity(
- intent, KeyguardUpdateMonitor.getCurrentUser()
+ intent, selectedUserInteractor.getSelectedUserId()
)
if (CameraIntents.isSecureCameraIntent(intent) && !wouldLaunchResolverActivity) {
uiExecutor.execute {
@@ -102,7 +101,7 @@
val activityOptions = ActivityOptions.makeBasic()
activityOptions.setDisallowEnterPictureInPictureWhileLaunching(true)
activityOptions.rotationAnimationHint =
- WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS
+ WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS
try {
activityTaskManager.startActivityAsUser(
null,
@@ -116,7 +115,7 @@
Intent.FLAG_ACTIVITY_NEW_TASK,
null,
activityOptions.toBundle(),
- userTracker.userId,
+ selectedUserInteractor.getSelectedUserId(true),
)
} catch (e: RemoteException) {
Log.w(
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index 39c01f7..a6b073d 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -37,12 +37,15 @@
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.sensors.ThresholdSensor;
import com.android.systemui.util.sensors.ThresholdSensorEvent;
import com.android.systemui.util.time.SystemClock;
+import dagger.Lazy;
+
import java.util.Collections;
import javax.inject.Inject;
@@ -66,6 +69,7 @@
private final DockManager mDockManager;
private final DelayableExecutor mMainExecutor;
private final SystemClock mSystemClock;
+ private final Lazy<SelectedUserInteractor> mUserInteractor;
private int mState;
private boolean mShowingAod;
@@ -93,7 +97,7 @@
public void onBiometricAuthenticated(int userId,
BiometricSourceType biometricSourceType,
boolean isStrongBiometric) {
- if (userId == KeyguardUpdateMonitor.getCurrentUser()
+ if (userId == mUserInteractor.get().getSelectedUserId()
&& biometricSourceType == BiometricSourceType.FACE) {
mFalsingDataProvider.setJustUnlockedWithFace(true);
}
@@ -136,7 +140,8 @@
BatteryController batteryController,
DockManager dockManager,
@Main DelayableExecutor mainExecutor,
- SystemClock systemClock) {
+ SystemClock systemClock,
+ Lazy<SelectedUserInteractor> userInteractor) {
mFalsingDataProvider = falsingDataProvider;
mFalsingManager = falsingManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -148,6 +153,7 @@
mDockManager = dockManager;
mMainExecutor = mainExecutor;
mSystemClock = systemClock;
+ mUserInteractor = userInteractor;
mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
mProximitySensor.setDelay(SensorManager.SENSOR_DELAY_GAME);
diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
index bf4fba8..0bf5069 100644
--- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
@@ -26,11 +26,13 @@
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.colorextraction.types.ExtractionType;
import com.android.internal.colorextraction.types.Tonal;
-import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+
+import dagger.Lazy;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -48,19 +50,22 @@
private boolean mHasMediaArtwork;
private final GradientColors mNeutralColorsLock;
private final GradientColors mBackdropColors;
+ private Lazy<SelectedUserInteractor> mUserInteractor;
@Inject
public SysuiColorExtractor(
Context context,
ConfigurationController configurationController,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ Lazy<SelectedUserInteractor> userInteractor) {
this(
context,
new Tonal(context),
configurationController,
context.getSystemService(WallpaperManager.class),
dumpManager,
- false /* immediately */);
+ false /* immediately */,
+ userInteractor);
}
@VisibleForTesting
@@ -70,7 +75,8 @@
ConfigurationController configurationController,
WallpaperManager wallpaperManager,
DumpManager dumpManager,
- boolean immediately) {
+ boolean immediately,
+ Lazy<SelectedUserInteractor> userInteractor) {
super(context, type, immediately, wallpaperManager);
mTonal = type instanceof Tonal ? (Tonal) type : new Tonal(context);
mNeutralColorsLock = new GradientColors();
@@ -79,6 +85,7 @@
mBackdropColors = new GradientColors();
mBackdropColors.setMainColor(Color.BLACK);
+ mUserInteractor = userInteractor;
// Listen to all users instead of only the current one.
if (wallpaperManager.isWallpaperSupported()) {
@@ -100,7 +107,7 @@
@Override
public void onColorsChanged(WallpaperColors colors, int which, int userId) {
- if (userId != KeyguardUpdateMonitor.getCurrentUser()) {
+ if (userId != mUserInteractor.get().getSelectedUserId()) {
// Colors do not belong to current user, ignoring.
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetMetadata.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetMetadata.kt
index f9c4f29..1a214ba 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetMetadata.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetMetadata.kt
@@ -16,7 +16,7 @@
package com.android.systemui.communal.data.model
-import com.android.systemui.communal.shared.CommunalContentSize
+import com.android.systemui.communal.shared.model.CommunalContentSize
/** Metadata for the default widgets */
data class CommunalWidgetMetadata(
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index f13b62f..77025dc 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -20,6 +20,7 @@
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProviderInfo
import android.content.BroadcastReceiver
+import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
@@ -28,11 +29,12 @@
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.communal.data.model.CommunalWidgetMetadata
-import com.android.systemui.communal.shared.CommunalAppWidgetInfo
-import com.android.systemui.communal.shared.CommunalContentSize
+import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
+import com.android.systemui.communal.shared.model.CommunalContentSize
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
@@ -43,6 +45,7 @@
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
/** Encapsulates the state of widgets for communal mode. */
@@ -52,6 +55,9 @@
/** Widgets that are allowed to render in the glanceable hub */
val communalWidgetAllowlist: List<CommunalWidgetMetadata>
+
+ /** A flow of information about all the communal widgets to show. */
+ val communalWidgets: Flow<List<CommunalWidgetContentModel>>
}
@SysUISingleton
@@ -67,7 +73,7 @@
private val userManager: UserManager,
private val userTracker: UserTracker,
@CommunalLog logBuffer: LogBuffer,
- featureFlags: FeatureFlags,
+ featureFlags: FeatureFlagsClassic,
) : CommunalWidgetRepository {
companion object {
const val TAG = "CommunalWidgetRepository"
@@ -88,49 +94,59 @@
// Widgets that should be rendered in communal mode.
private val widgets: HashMap<Int, CommunalAppWidgetInfo> = hashMapOf()
- private val isUserUnlocked: Flow<Boolean> = callbackFlow {
- if (!featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) {
- awaitClose()
- }
-
- fun isUserUnlockingOrUnlocked(): Boolean {
- return userManager.isUserUnlockingOrUnlocked(userTracker.userHandle)
- }
-
- fun send() {
- trySendWithFailureLogging(isUserUnlockingOrUnlocked(), TAG)
- }
-
- if (isUserUnlockingOrUnlocked()) {
- send()
- awaitClose()
- } else {
- val receiver =
- object : BroadcastReceiver() {
- override fun onReceive(context: Context?, intent: Intent?) {
- send()
- }
+ private val isUserUnlocked: Flow<Boolean> =
+ callbackFlow {
+ if (!communalRepository.isCommunalEnabled) {
+ awaitClose()
}
- broadcastDispatcher.registerReceiver(
- receiver,
- IntentFilter(Intent.ACTION_USER_UNLOCKED),
- )
+ fun isUserUnlockingOrUnlocked(): Boolean {
+ return userManager.isUserUnlockingOrUnlocked(userTracker.userHandle)
+ }
- awaitClose { broadcastDispatcher.unregisterReceiver(receiver) }
+ fun send() {
+ trySendWithFailureLogging(isUserUnlockingOrUnlocked(), TAG)
+ }
+
+ if (isUserUnlockingOrUnlocked()) {
+ send()
+ awaitClose()
+ } else {
+ val receiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ send()
+ }
+ }
+
+ broadcastDispatcher.registerReceiver(
+ receiver,
+ IntentFilter(Intent.ACTION_USER_UNLOCKED),
+ )
+
+ awaitClose { broadcastDispatcher.unregisterReceiver(receiver) }
+ }
+ }
+ .distinctUntilChanged()
+
+ private val isHostActive: Flow<Boolean> =
+ isUserUnlocked.map {
+ if (it) {
+ startListening()
+ true
+ } else {
+ stopListening()
+ clearWidgets()
+ false
+ }
}
- }
override val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?> =
- isUserUnlocked.map { isUserUnlocked ->
- if (!isUserUnlocked) {
- clearWidgets()
- stopListening()
+ isHostActive.map { isHostActive ->
+ if (!isHostActive || !featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) {
return@map null
}
- startListening()
-
val providerInfo =
appWidgetManager.installedProviders.find {
it.loadLabel(packageManager).equals(WIDGET_LABEL)
@@ -144,6 +160,42 @@
return@map addWidget(providerInfo)
}
+ override val communalWidgets: Flow<List<CommunalWidgetContentModel>> =
+ isHostActive.map { isHostActive ->
+ if (!isHostActive) {
+ return@map emptyList()
+ }
+
+ // The allowlist should be fetched from the local database with all the metadata tied to
+ // a widget, including an appWidgetId if it has been bound. Before the database is set
+ // up, we are going to use the app widget host as the source of truth for bound widgets,
+ // and rebind each time on boot.
+
+ // Remove all previously bound widgets.
+ appWidgetHost.appWidgetIds.forEach { appWidgetHost.deleteAppWidgetId(it) }
+
+ val inventory = mutableListOf<CommunalWidgetContentModel>()
+
+ // Bind all widgets from the allowlist.
+ communalWidgetAllowlist.forEach {
+ val id = appWidgetHost.allocateAppWidgetId()
+ appWidgetManager.bindAppWidgetId(
+ id,
+ ComponentName.unflattenFromString(it.componentName),
+ )
+
+ inventory.add(
+ CommunalWidgetContentModel(
+ appWidgetId = id,
+ providerInfo = appWidgetManager.getAppWidgetInfo(id),
+ priority = it.priority,
+ )
+ )
+ }
+
+ return@map inventory.toList()
+ }
+
private fun getWidgetAllowlist(): List<CommunalWidgetMetadata> {
val componentNames =
applicationContext.resources.getStringArray(R.array.config_communalWidgetAllowlist)
@@ -151,7 +203,7 @@
CommunalWidgetMetadata(
componentName = name,
priority = componentNames.size - index,
- sizes = listOf(CommunalContentSize.HALF)
+ sizes = listOf(CommunalContentSize.HALF),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 04bb6ae..6238707 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -18,7 +18,8 @@
import com.android.systemui.communal.data.repository.CommunalRepository
import com.android.systemui.communal.data.repository.CommunalWidgetRepository
-import com.android.systemui.communal.shared.CommunalAppWidgetInfo
+import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -38,4 +39,12 @@
/** A flow of info about the widget to be displayed, or null if widget is unavailable. */
val appWidgetInfo: Flow<CommunalAppWidgetInfo?> = widgetRepository.stopwatchAppWidgetInfo
+
+ /**
+ * A flow of information about widgets to be shown in communal hub.
+ *
+ * Currently only showing persistent widgets that have been bound to the app widget service
+ * (have an allocated id).
+ */
+ val widgetContent: Flow<List<CommunalWidgetContentModel>> = widgetRepository.communalWidgets
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalContentSize.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalContentSize.kt
deleted file mode 100644
index 0bd7d86..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalContentSize.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.android.systemui.communal.shared
-
-/** Supported sizes for communal content in the layout grid. */
-enum class CommunalContentSize {
- FULL,
- HALF,
- THIRD,
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalAppWidgetInfo.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt
rename to packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalAppWidgetInfo.kt
index 0803a01..109ed2d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalAppWidgetInfo.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.systemui.communal.shared
+package com.android.systemui.communal.shared.model
import android.appwidget.AppWidgetProviderInfo
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentCategory.kt
similarity index 67%
copy from packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt
copy to packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentCategory.kt
index 0803a01..7f05b9c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentCategory.kt
@@ -12,15 +12,14 @@
* 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.communal.shared
+package com.android.systemui.communal.shared.model
-import android.appwidget.AppWidgetProviderInfo
+enum class CommunalContentCategory {
+ /** The content persists in the communal hub until removed by the user. */
+ PERSISTENT,
-/** A data class that stores info about an app widget that displays in communal mode. */
-data class CommunalAppWidgetInfo(
- val providerInfo: AppWidgetProviderInfo,
- val appWidgetId: Int,
-)
+ /** The content temporarily shows up in the communal hub when certain conditions are met. */
+ TRANSIENT,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
similarity index 63%
copy from packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt
copy to packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
index 0803a01..39a6476 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
@@ -12,15 +12,18 @@
* 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.communal.shared
+package com.android.systemui.communal.shared.model
-import android.appwidget.AppWidgetProviderInfo
+/** Supported sizes for communal content in the layout grid. */
+enum class CommunalContentSize {
+ /** Content takes the full height of the column. */
+ FULL,
-/** A data class that stores info about an app widget that displays in communal mode. */
-data class CommunalAppWidgetInfo(
- val providerInfo: AppWidgetProviderInfo,
- val appWidgetId: Int,
-)
+ /** Content takes half of the height of the column. */
+ HALF,
+
+ /** Content takes a third of the height of the column. */
+ THIRD,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
similarity index 80%
copy from packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt
copy to packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
index 0803a01..e141dc4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
@@ -12,15 +12,15 @@
* 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.communal.shared
+package com.android.systemui.communal.shared.model
import android.appwidget.AppWidgetProviderInfo
-/** A data class that stores info about an app widget that displays in communal mode. */
-data class CommunalAppWidgetInfo(
- val providerInfo: AppWidgetProviderInfo,
+/** Encapsulates data for a communal widget. */
+data class CommunalWidgetContentModel(
val appWidgetId: Int,
+ val providerInfo: AppWidgetProviderInfo,
+ val priority: Int,
)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/adapter/CommunalWidgetViewAdapter.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/adapter/CommunalWidgetViewAdapter.kt
index 2a08d7f..0daf7b5 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/adapter/CommunalWidgetViewAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/adapter/CommunalWidgetViewAdapter.kt
@@ -20,7 +20,7 @@
import android.appwidget.AppWidgetManager
import android.content.Context
import android.util.SizeF
-import com.android.systemui.communal.shared.CommunalAppWidgetInfo
+import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
import com.android.systemui.communal.ui.view.CommunalWidgetWrapper
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt
new file mode 100644
index 0000000..98060dc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt
@@ -0,0 +1,15 @@
+package com.android.systemui.communal.ui.model
+
+import android.view.View
+import com.android.systemui.communal.shared.model.CommunalContentSize
+
+/**
+ * Encapsulates data for a communal content that holds a view.
+ *
+ * This model stays in the UI layer.
+ */
+data class CommunalContentUiModel(
+ val view: View,
+ val size: CommunalContentSize,
+ val priority: Int,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index ddeb1d6..25c64ea 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -16,17 +16,43 @@
package com.android.systemui.communal.ui.viewmodel
+import android.appwidget.AppWidgetHost
+import android.content.Context
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor
+import com.android.systemui.communal.shared.model.CommunalContentSize
+import com.android.systemui.communal.ui.model.CommunalContentUiModel
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
@SysUISingleton
class CommunalViewModel
@Inject
constructor(
+ @Application private val context: Context,
+ private val appWidgetHost: AppWidgetHost,
+ communalInteractor: CommunalInteractor,
tutorialInteractor: CommunalTutorialInteractor,
) {
/** Whether communal hub should show tutorial content. */
val showTutorialContent: Flow<Boolean> = tutorialInteractor.isTutorialAvailable
+
+ /** List of widgets to be displayed in the communal hub. */
+ val widgetContent: Flow<List<CommunalContentUiModel>> =
+ communalInteractor.widgetContent.map {
+ it.map {
+ // TODO(b/306406256): As adding and removing widgets functionalities are
+ // supported, cache the host views so they're not recreated each time.
+ val hostView = appWidgetHost.createView(context, it.appWidgetId, it.providerInfo)
+ return@map CommunalContentUiModel(
+ view = hostView,
+ priority = it.priority,
+ // All widgets have HALF size.
+ size = CommunalContentSize.HALF,
+ )
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt
index 8fba342..d7bbea6 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalWidgetViewModel.kt
@@ -17,7 +17,7 @@
package com.android.systemui.communal.ui.viewmodel
import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.communal.shared.CommunalAppWidgetInfo
+import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
index 9221832b..4bdea75 100644
--- a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
+++ b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
@@ -79,4 +79,7 @@
context: Context,
viewModel: CommunalViewModel,
): View
+
+ /** Creates a container that hosts the communal UI and handles gesture transitions. */
+ fun createCommunalContainer(context: Context, viewModel: CommunalViewModel): View
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java b/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java
index 5eb9808..9c13a8c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java
@@ -18,6 +18,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.doze.dagger.DozeScope;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import javax.inject.Inject;
@@ -28,16 +29,19 @@
public class DozeAuthRemover implements DozeMachine.Part {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final SelectedUserInteractor mSelectedUserInteractor;
@Inject
- public DozeAuthRemover(KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ public DozeAuthRemover(KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SelectedUserInteractor selectedUserInteractor) {
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mSelectedUserInteractor = selectedUserInteractor;
}
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
if (newState == DozeMachine.State.DOZE || newState == DozeMachine.State.DOZE_AOD) {
- int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ int currentUser = mSelectedUserInteractor.getSelectedUserId();
if (mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(currentUser)) {
mKeyguardUpdateMonitor.clearBiometricRecognized();
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 7da2cf1..ba57918 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -31,13 +31,13 @@
import androidx.annotation.Nullable;
-import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.UdfpsController;
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.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.wakelock.SettableWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -81,6 +81,7 @@
@Nullable private UdfpsController mUdfpsController;
private final DozeLog mDozeLog;
private final DozeScreenBrightness mDozeScreenBrightness;
+ private final SelectedUserInteractor mSelectedUserInteractor;
private int mPendingScreenState = Display.STATE_UNKNOWN;
private SettableWakeLock mWakeLock;
@@ -95,7 +96,8 @@
AuthController authController,
Provider<UdfpsController> udfpsControllerProvider,
DozeLog dozeLog,
- DozeScreenBrightness dozeScreenBrightness) {
+ DozeScreenBrightness dozeScreenBrightness,
+ SelectedUserInteractor selectedUserInteractor) {
mDozeService = service;
mHandler = handler;
mParameters = parameters;
@@ -105,6 +107,7 @@
mUdfpsControllerProvider = udfpsControllerProvider;
mDozeLog = dozeLog;
mDozeScreenBrightness = dozeScreenBrightness;
+ mSelectedUserInteractor = selectedUserInteractor;
updateUdfpsController();
if (mUdfpsController == null) {
@@ -113,7 +116,7 @@
}
private void updateUdfpsController() {
- if (mAuthController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())) {
+ if (mAuthController.isUdfpsEnrolled(mSelectedUserInteractor.getSelectedUserId())) {
mUdfpsController = mUdfpsControllerProvider.get();
} else {
mUdfpsController = null;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 07efbfe..3194942 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -48,12 +48,11 @@
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
-import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.plugins.SensorManagerPlugin;
-import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.settings.SecureSettings;
@@ -101,7 +100,7 @@
private final SecureSettings mSecureSettings;
private final DevicePostureController mDevicePostureController;
private final AuthController mAuthController;
- private final UserTracker mUserTracker;
+ private final SelectedUserInteractor mSelectedUserInteractor;
private final boolean mScreenOffUdfpsEnabled;
// Sensors
@@ -158,7 +157,7 @@
SecureSettings secureSettings,
AuthController authController,
DevicePostureController devicePostureController,
- UserTracker userTracker
+ SelectedUserInteractor selectedUserInteractor
) {
mSensorManager = sensorManager;
mConfig = config;
@@ -171,15 +170,15 @@
mProximitySensor.setTag(TAG);
mSelectivelyRegisterProxSensors = dozeParameters.getSelectivelyRegisterSensorsUsingProx();
mListeningProxSensors = !mSelectivelyRegisterProxSensors;
+ mSelectedUserInteractor = selectedUserInteractor;
mScreenOffUdfpsEnabled =
- config.screenOffUdfpsEnabled(KeyguardUpdateMonitor.getCurrentUser());
+ config.screenOffUdfpsEnabled(mSelectedUserInteractor.getSelectedUserId());
mDevicePostureController = devicePostureController;
mDevicePosture = mDevicePostureController.getDevicePosture();
mAuthController = authController;
- mUserTracker = userTracker;
mUdfpsEnrolled =
- mAuthController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser());
+ mAuthController.isUdfpsEnrolled(mSelectedUserInteractor.getSelectedUserId());
mAuthController.addCallback(mAuthControllerCallback);
mTriggerSensors = new TriggerSensor[] {
new TriggerSensor(
@@ -255,7 +254,8 @@
new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
mConfig.wakeScreenGestureAvailable()
- && mConfig.alwaysOnEnabled(mUserTracker.getUserId()),
+ && mConfig.alwaysOnEnabled(
+ mSelectedUserInteractor.getSelectedUserId(true)),
DozeLog.REASON_SENSOR_WAKE_UP_PRESENCE,
false /* reports touch coordinates */,
false /* touchscreen */
@@ -296,12 +296,13 @@
private boolean udfpsLongPressConfigured() {
return mUdfpsEnrolled
- && (mConfig.alwaysOnEnabled(mUserTracker.getUserId()) || mScreenOffUdfpsEnabled);
+ && (mConfig.alwaysOnEnabled(mSelectedUserInteractor.getSelectedUserId(true))
+ || mScreenOffUdfpsEnabled);
}
private boolean quickPickUpConfigured() {
return mUdfpsEnrolled
- && mConfig.quickPickupSensorEnabled(KeyguardUpdateMonitor.getCurrentUser());
+ && mConfig.quickPickupSensorEnabled(mSelectedUserInteractor.getSelectedUserId());
}
/**
@@ -471,7 +472,7 @@
private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange, Collection<Uri> uris, int flags, int userId) {
- if (userId != mUserTracker.getUserId()) {
+ if (userId != mSelectedUserInteractor.getSelectedUserId(true)) {
return;
}
for (TriggerSensor s : mTriggerSensors) {
@@ -697,13 +698,13 @@
}
protected boolean enabledBySetting() {
- if (!mConfig.enabled(mUserTracker.getUserId())) {
+ if (!mConfig.enabled(mSelectedUserInteractor.getSelectedUserId(true))) {
return false;
} else if (TextUtils.isEmpty(mSetting)) {
return true;
}
return mSecureSettings.getIntForUser(mSetting, mSettingDefault ? 1 : 0,
- mUserTracker.getUserId()) != 0;
+ mSelectedUserInteractor.getSelectedUserId(true)) != 0;
}
@Override
@@ -873,7 +874,7 @@
private void updateUdfpsEnrolled() {
mUdfpsEnrolled = mAuthController.isUdfpsEnrolled(
- KeyguardUpdateMonitor.getCurrentUser());
+ mSelectedUserInteractor.getSelectedUserId());
for (TriggerSensor sensor : mTriggerSensors) {
if (REASON_SENSOR_QUICK_PICKUP == sensor.mPulseReason) {
sensor.setConfigured(quickPickUpConfigured());
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 85272a6..795c3d4 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -50,6 +50,7 @@
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.Assert;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximityCheck;
@@ -102,6 +103,7 @@
private final AuthController mAuthController;
private final KeyguardStateController mKeyguardStateController;
private final UserTracker mUserTracker;
+ private final SelectedUserInteractor mSelectedUserInteractor;
private final UiEventLogger mUiEventLogger;
private long mNotificationPulseTime;
@@ -201,7 +203,8 @@
SessionTracker sessionTracker,
KeyguardStateController keyguardStateController,
DevicePostureController devicePostureController,
- UserTracker userTracker) {
+ UserTracker userTracker,
+ SelectedUserInteractor selectedUserInteractor) {
mContext = context;
mDozeHost = dozeHost;
mConfig = config;
@@ -213,7 +216,7 @@
mDozeSensors = new DozeSensors(mContext.getResources(), mSensorManager, dozeParameters,
config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor,
- secureSettings, authController, devicePostureController, userTracker);
+ secureSettings, authController, devicePostureController, selectedUserInteractor);
mDockManager = dockManager;
mProxCheck = proxCheck;
mDozeLog = dozeLog;
@@ -222,6 +225,7 @@
mUiEventLogger = uiEventLogger;
mKeyguardStateController = keyguardStateController;
mUserTracker = userTracker;
+ mSelectedUserInteractor = selectedUserInteractor;
}
@Override
@@ -246,7 +250,7 @@
return;
}
mNotificationPulseTime = SystemClock.elapsedRealtime();
- if (!mConfig.pulseOnNotificationEnabled(mUserTracker.getUserId())) {
+ if (!mConfig.pulseOnNotificationEnabled(mSelectedUserInteractor.getSelectedUserId(true))) {
runIfNotNull(onPulseSuppressedListener);
mDozeLog.tracePulseDropped("pulseOnNotificationsDisabled");
return;
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 472cc24..c91c9ac 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -180,6 +180,10 @@
@JvmField
val NEW_AOD_TRANSITION = unreleasedFlag("new_aod_transition", teamfood = true)
+ // TODO(b/305984787):
+ @JvmField
+ val REFACTOR_GETCURRENTUSER = unreleasedFlag("refactor_getcurrentuser", teamfood = true)
+
/** Flag to control the migration of face auth to modern architecture. */
// TODO(b/262838215): Tracking bug
@JvmField val FACE_AUTH_REFACTOR = releasedFlag("face_auth_refactor")
@@ -259,7 +263,7 @@
// TODO(b/290652751): Tracking bug.
@JvmField
val MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA =
- releasedFlag("migrate_split_keyguard_bottom_area")
+ unreleasedFlag("migrate_split_keyguard_bottom_area", teamfood = true)
// TODO(b/297037052): Tracking bug.
@JvmField
@@ -274,7 +278,7 @@
/** Migrate the lock icon view to the new keyguard root view. */
// TODO(b/286552209): Tracking bug.
- @JvmField val MIGRATE_LOCK_ICON = releasedFlag("migrate_lock_icon")
+ @JvmField val MIGRATE_LOCK_ICON = unreleasedFlag("migrate_lock_icon", teamfood = true)
// TODO(b/288276738): Tracking bug.
@JvmField val WIDGET_ON_KEYGUARD = unreleasedFlag("widget_on_keyguard")
@@ -769,11 +773,10 @@
// TODO(b/283740863): Tracking Bug
@JvmField
- val ENABLE_NEW_PRIVACY_DIALOG =
- unreleasedFlag("enable_new_privacy_dialog", teamfood = true)
+ val ENABLE_NEW_PRIVACY_DIALOG = releasedFlag("enable_new_privacy_dialog")
// TODO(b/289573946): Tracking Bug
- @JvmField val PRECOMPUTED_TEXT = unreleasedFlag("precomputed_text", teamfood = true)
+ @JvmField val PRECOMPUTED_TEXT = releasedFlag("precomputed_text")
// TODO(b/302087895): Tracking Bug
@JvmField val CALL_LAYOUT_ASYNC_SET_DATA = unreleasedFlag("call_layout_async_set_data")
@@ -796,8 +799,7 @@
/** TODO(b/296223317): Enables the new keyguard presentation containing a clock. */
@JvmField
- val ENABLE_CLOCK_KEYGUARD_PRESENTATION =
- unreleasedFlag("enable_clock_keyguard_presentation", teamfood = true)
+ val ENABLE_CLOCK_KEYGUARD_PRESENTATION = releasedFlag("enable_clock_keyguard_presentation")
/** Enable the Compose implementation of the PeopleSpaceActivity. */
@JvmField
@@ -817,8 +819,7 @@
// TODO(b/287205379): Tracking bug
@JvmField
- val QS_CONTAINER_GRAPH_OPTIMIZER = unreleasedFlag( "qs_container_graph_optimizer",
- teamfood = true)
+ val QS_CONTAINER_GRAPH_OPTIMIZER = releasedFlag( "qs_container_graph_optimizer")
/** Enable showing a dialog when clicking on Quick Settings bluetooth tile. */
@JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index c6c1f79..5cc2e0a 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -134,6 +134,7 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.telephony.TelephonyListenerManager;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.EmergencyDialerConstants;
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.settings.GlobalSettings;
@@ -195,6 +196,7 @@
private final IDreamManager mDreamManager;
private final DevicePolicyManager mDevicePolicyManager;
private final LockPatternUtils mLockPatternUtils;
+ private final SelectedUserInteractor mSelectedUserInteractor;
private final TelephonyListenerManager mTelephonyListenerManager;
private final KeyguardStateController mKeyguardStateController;
private final BroadcastDispatcher mBroadcastDispatcher;
@@ -364,7 +366,8 @@
PackageManager packageManager,
ShadeController shadeController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- DialogLaunchAnimator dialogLaunchAnimator) {
+ DialogLaunchAnimator dialogLaunchAnimator,
+ SelectedUserInteractor selectedUserInteractor) {
mContext = context;
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -399,6 +402,7 @@
mShadeController = shadeController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mDialogLaunchAnimator = dialogLaunchAnimator;
+ mSelectedUserInteractor = selectedUserInteractor;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -713,7 +717,8 @@
mUiEventLogger,
mShadeController,
mKeyguardUpdateMonitor,
- mLockPatternUtils);
+ mLockPatternUtils,
+ mSelectedUserInteractor);
dialog.setOnDismissListener(this);
dialog.setOnShowListener(this);
@@ -2222,6 +2227,7 @@
private GestureDetector mGestureDetector;
private final ShadeController mShadeController;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private SelectedUserInteractor mSelectedUserInteractor;
private LockPatternUtils mLockPatternUtils;
private float mWindowDimAmount;
@@ -2300,7 +2306,8 @@
UiEventLogger uiEventLogger,
ShadeController shadeController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- LockPatternUtils lockPatternUtils) {
+ LockPatternUtils lockPatternUtils,
+ SelectedUserInteractor selectedUserInteractor) {
// We set dismissOnDeviceLock to false because we have a custom broadcast receiver to
// dismiss this dialog when the device is locked.
super(context, themeRes, false /* dismissOnDeviceLock */);
@@ -2321,6 +2328,7 @@
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mGestureDetector = new GestureDetector(mContext, mGestureListener);
+ mSelectedUserInteractor = selectedUserInteractor;
}
@Override
@@ -2453,10 +2461,10 @@
}
// If user entered from the lock screen and smart lock was enabled, disable it
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int user = mSelectedUserInteractor.getSelectedUserId();
boolean userHasTrust = mKeyguardUpdateMonitor.getUserHasTrust(user);
if (mKeyguardShowing && userHasTrust) {
- mLockPatternUtils.requireCredentialEntry(KeyguardUpdateMonitor.getCurrentUser());
+ mLockPatternUtils.requireCredentialEntry(user);
showSmartLockDisabledMessage();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index b45613e..4779895 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -35,6 +35,8 @@
import static android.view.WindowManager.TransitionOldType;
import static android.view.WindowManager.TransitionType;
+import static com.android.systemui.flags.Flags.REFACTOR_GETCURRENTUSER;
+
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -76,13 +78,13 @@
import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.power.shared.model.ScreenPowerState;
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier;
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindViewBinder;
import com.android.systemui.keyguard.ui.binder.WindowManagerLockscreenVisibilityViewBinder;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSurfaceBehindViewModel;
import com.android.systemui.keyguard.ui.viewmodel.WindowManagerLockscreenVisibilityViewModel;
import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.power.shared.model.ScreenPowerState;
import com.android.systemui.settings.DisplayTracker;
import com.android.wm.shell.transition.ShellTransitions;
import com.android.wm.shell.transition.Transitions;
@@ -599,11 +601,18 @@
mKeyguardViewMediator.setSwitchingUser(switching);
}
+ /**
+ * @deprecated This binder call is not listened to anymore. Instead the current user is
+ * tracked in SelectedUserInteractor.getSelectedUserId()
+ */
@Override // Binder interface
+ @Deprecated
public void setCurrentUser(int userId) {
- trace("setCurrentUser userId=" + userId);
+ trace("Deprecated/NOT USED: setCurrentUser userId=" + userId);
checkPermission();
- mKeyguardViewMediator.setCurrentUser(userId);
+ if (!mFlags.isEnabled(REFACTOR_GETCURRENTUSER)) {
+ mKeyguardViewMediator.setCurrentUser(userId);
+ }
}
@Override // Binder interface
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 39742a0..e893c63 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -38,6 +38,7 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.flags.Flags.REFACTOR_GETCURRENTUSER;
import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
@@ -163,6 +164,7 @@
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.SecureSettings;
@@ -617,6 +619,9 @@
public void onUserSwitching(int userId) {
if (DEBUG) Log.d(TAG, String.format("onUserSwitching %d", userId));
synchronized (KeyguardViewMediator.this) {
+ if (mFeatureFlags.isEnabled(REFACTOR_GETCURRENTUSER)) {
+ notifyTrustedChangedLocked(mUpdateMonitor.getUserHasTrust(userId));
+ }
resetKeyguardDonePendingLocked();
dismiss(null /* callback */, null /* message */);
adjustStatusBarLocked();
@@ -742,7 +747,7 @@
@Override
public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
- final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ final int currentUser = mSelectedUserInteractor.getSelectedUserId();
if (mLockPatternUtils.isSecure(currentUser)) {
mLockPatternUtils.getDevicePolicyManager().reportFailedBiometricAttempt(
currentUser);
@@ -760,7 +765,7 @@
@Override
public void onTrustChanged(int userId) {
- if (userId == KeyguardUpdateMonitor.getCurrentUser()) {
+ if (userId == mSelectedUserInteractor.getSelectedUserId()) {
synchronized (KeyguardViewMediator.this) {
notifyTrustedChangedLocked(mUpdateMonitor.getUserHasTrust(userId));
}
@@ -769,7 +774,7 @@
@Override
public void onStrongAuthStateChanged(int userId) {
- if (mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) {
+ if (mLockPatternUtils.isUserInLockdown(mSelectedUserInteractor.getSelectedUserId())) {
doKeyguardLocked(null);
}
}
@@ -784,7 +789,7 @@
@Override
public void keyguardDone(int targetUserId) {
- if (targetUserId != KeyguardUpdateMonitor.getCurrentUser()) {
+ if (targetUserId != mSelectedUserInteractor.getSelectedUserId()) {
return;
}
if (DEBUG) Log.d(TAG, "keyguardDone");
@@ -807,7 +812,7 @@
public void keyguardDonePending(int targetUserId) {
Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardDonePending");
if (DEBUG) Log.d(TAG, "keyguardDonePending");
- if (targetUserId != KeyguardUpdateMonitor.getCurrentUser()) {
+ if (targetUserId != mSelectedUserInteractor.getSelectedUserId()) {
Trace.endSection();
return;
}
@@ -888,7 +893,7 @@
@Override
public int getBouncerPromptReason() {
- int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ int currentUser = mSelectedUserInteractor.getSelectedUserId();
boolean trustAgentsEnabled = mUpdateMonitor.isTrustUsuallyManaged(currentUser);
boolean biometricsEnrolled =
mUpdateMonitor.isUnlockingWithBiometricsPossible(currentUser);
@@ -1316,6 +1321,7 @@
private DeviceConfigProxy mDeviceConfig;
private DozeParameters mDozeParameters;
+ private SelectedUserInteractor mSelectedUserInteractor;
private final KeyguardStateController mKeyguardStateController;
private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
@@ -1396,7 +1402,8 @@
@Main CoroutineDispatcher mainDispatcher,
Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel,
SystemPropertiesHelper systemPropertiesHelper,
- Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager) {
+ Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager,
+ SelectedUserInteractor selectedUserInteractor) {
mContext = context;
mUserTracker = userTracker;
mFalsingCollector = falsingCollector;
@@ -1436,6 +1443,7 @@
mInGestureNavigationMode = QuickStepContract.isGesturalMode(mode);
}));
mDozeParameters = dozeParameters;
+ mSelectedUserInteractor = selectedUserInteractor;
mStatusBarStateController = statusBarStateController;
statusBarStateController.addCallback(this);
@@ -1493,14 +1501,17 @@
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
- KeyguardUpdateMonitor.setCurrentUser(mUserTracker.getUserId());
+ if (!mFeatureFlags.isEnabled(REFACTOR_GETCURRENTUSER)) {
+ KeyguardUpdateMonitor.setCurrentUser(mUserTracker.getUserId());
+ }
// Assume keyguard is showing (unless it's disabled) until we know for sure, unless Keyguard
// is disabled.
if (isKeyguardServiceEnabled()) {
setShowingLocked(!shouldWaitForProvisioning()
&& !mLockPatternUtils.isLockScreenDisabled(
- KeyguardUpdateMonitor.getCurrentUser()), true /* forceCallbacks */);
+ mSelectedUserInteractor.getSelectedUserId()),
+ true /* forceCallbacks */);
} else {
// The system's keyguard is disabled or missing.
setShowingLocked(false /* showing */, true /* forceCallbacks */);
@@ -1622,11 +1633,11 @@
// Lock immediately based on setting if secure (user has a pin/pattern/password).
// This also "locks" the device when not secure to provide easy access to the
// camera while preventing unwanted input.
- int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ int currentUser = mSelectedUserInteractor.getSelectedUserId();
final boolean lockImmediately =
mLockPatternUtils.getPowerButtonInstantlyLocks(currentUser)
|| !mLockPatternUtils.isSecure(currentUser);
- long timeout = getLockTimeout(KeyguardUpdateMonitor.getCurrentUser());
+ long timeout = getLockTimeout(mSelectedUserInteractor.getSelectedUserId());
mLockLater = false;
if (mShowing && !mKeyguardStateController.isKeyguardGoingAway()) {
// If we are going to sleep but the keyguard is showing (and will continue to be
@@ -1807,7 +1818,7 @@
}
private void doKeyguardLaterLocked() {
- long timeout = getLockTimeout(KeyguardUpdateMonitor.getCurrentUser());
+ long timeout = getLockTimeout(mSelectedUserInteractor.getSelectedUserId());
if (timeout == 0) {
doKeyguardLocked(null);
} else {
@@ -1916,7 +1927,7 @@
private void maybeSendUserPresentBroadcast() {
if (mSystemReady && mLockPatternUtils.isLockScreenDisabled(
- KeyguardUpdateMonitor.getCurrentUser())) {
+ mSelectedUserInteractor.getSelectedUserId())) {
// Lock screen is disabled because the user has set the preference to "None".
// In this case, send out ACTION_USER_PRESENT here instead of in
// handleKeyguardDone()
@@ -1925,7 +1936,7 @@
// Skipping the lockscreen because we're not yet provisioned, but we still need to
// notify the StrongAuthTracker that it's now safe to run trust agents, in case the
// user sets a credential later.
- mLockPatternUtils.userPresent(KeyguardUpdateMonitor.getCurrentUser());
+ mLockPatternUtils.userPresent(mSelectedUserInteractor.getSelectedUserId());
}
}
@@ -1966,7 +1977,8 @@
mExternallyEnabled = enabled;
if (!enabled && mShowing) {
- if (mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) {
+ if (mLockPatternUtils.isUserInLockdown(
+ mSelectedUserInteractor.getSelectedUserId())) {
Log.d(TAG, "keyguardEnabled(false) overridden by user lockdown");
return;
}
@@ -2197,7 +2209,8 @@
private void doKeyguardLocked(Bundle options) {
// if another app is disabling us, don't show
if (!mExternallyEnabled
- && !mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) {
+ && !mLockPatternUtils.isUserInLockdown(
+ mSelectedUserInteractor.getSelectedUserId())) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
mNeedToReshowWhenReenabled = true;
@@ -2253,7 +2266,7 @@
}
boolean forceShow = options != null && options.getBoolean(OPTION_FORCE_SHOW, false);
- if (mLockPatternUtils.isLockScreenDisabled(KeyguardUpdateMonitor.getCurrentUser())
+ if (mLockPatternUtils.isLockScreenDisabled(mSelectedUserInteractor.getSelectedUserId())
&& !lockedOrMissing && !forceShow) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
return;
@@ -2384,7 +2397,7 @@
}
public boolean isSecure() {
- return isSecure(KeyguardUpdateMonitor.getCurrentUser());
+ return isSecure(mSelectedUserInteractor.getSelectedUserId());
}
public boolean isSecure(int userId) {
@@ -2605,7 +2618,7 @@
*/
private void handleKeyguardDone() {
Trace.beginSection("KeyguardViewMediator#handleKeyguardDone");
- final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ final int currentUser = mSelectedUserInteractor.getSelectedUserId();
mUiBgExecutor.execute(() -> {
if (mLockPatternUtils.isSecure(currentUser)) {
mLockPatternUtils.getDevicePolicyManager().reportKeyguardDismissed(currentUser);
@@ -2631,7 +2644,7 @@
private void sendUserPresentBroadcast() {
synchronized (this) {
if (mBootCompleted) {
- int currentUserId = KeyguardUpdateMonitor.getCurrentUser();
+ int currentUserId = mSelectedUserInteractor.getSelectedUserId();
final UserHandle currentUser = new UserHandle(currentUserId);
final UserManager um = (UserManager) mContext.getSystemService(
Context.USER_SERVICE);
@@ -2679,7 +2692,7 @@
private void playSound(int soundId) {
if (soundId == 0) return;
int lockscreenSoundsEnabled = mSystemSettings.getIntForUser(LOCKSCREEN_SOUNDS_ENABLED, 1,
- KeyguardUpdateMonitor.getCurrentUser());
+ mSelectedUserInteractor.getSelectedUserId());
if (lockscreenSoundsEnabled == 1) {
mLockSounds.stop(mLockSoundStreamId);
@@ -2732,7 +2745,7 @@
*/
private void handleShow(Bundle options) {
Trace.beginSection("KeyguardViewMediator#handleShow");
- final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ final int currentUser = mSelectedUserInteractor.getSelectedUserId();
if (mLockPatternUtils.isSecure(currentUser)) {
mLockPatternUtils.getDevicePolicyManager().reportKeyguardSecured(currentUser);
}
@@ -2787,7 +2800,7 @@
* Schedule 4-hour idle timeout for non-strong biometrics when the device is locked
*/
private void scheduleNonStrongBiometricIdleTimeout() {
- final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ final int currentUser = mSelectedUserInteractor.getSelectedUserId();
// If unlocking with non-strong (i.e. weak or convenience) biometrics is possible, schedule
// 4hr idle timeout after which non-strong biometrics can't be used to unlock device until
// unlocking with strong biometric or primary auth (i.e. PIN/pattern/password)
@@ -3378,7 +3391,8 @@
if (forceClearFlags) {
try {
mStatusBarService.disableForUser(flags, mStatusBarDisableToken,
- mContext.getPackageName(), mUserTracker.getUserId());
+ mContext.getPackageName(),
+ mSelectedUserInteractor.getSelectedUserId(true));
} catch (RemoteException e) {
Log.d(TAG, "Failed to force clear flags", e);
}
@@ -3405,7 +3419,8 @@
try {
mStatusBarService.disableForUser(flags, mStatusBarDisableToken,
- mContext.getPackageName(), mUserTracker.getUserId());
+ mContext.getPackageName(),
+ mSelectedUserInteractor.getSelectedUserId(true));
} catch (RemoteException e) {
Log.d(TAG, "Failed to set disable flags: " + flags, e);
}
@@ -3728,7 +3743,8 @@
for (int i = size - 1; i >= 0; i--) {
IKeyguardStateCallback callback = mKeyguardStateCallbacks.get(i);
try {
- callback.onShowingStateChanged(showing, KeyguardUpdateMonitor.getCurrentUser());
+ callback.onShowingStateChanged(showing,
+ mSelectedUserInteractor.getSelectedUserId());
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call onShowingStateChanged", e);
if (e instanceof DeadObjectException) {
@@ -3771,10 +3787,11 @@
mKeyguardStateCallbacks.add(callback);
try {
callback.onSimSecureStateChanged(mUpdateMonitor.isSimPinSecure());
- callback.onShowingStateChanged(mShowing, KeyguardUpdateMonitor.getCurrentUser());
+ callback.onShowingStateChanged(mShowing,
+ mSelectedUserInteractor.getSelectedUserId());
callback.onInputRestrictedStateChanged(mInputRestricted);
callback.onTrustedChanged(mUpdateMonitor.getUserHasTrust(
- KeyguardUpdateMonitor.getCurrentUser()));
+ mSelectedUserInteractor.getSelectedUserId()));
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call to IKeyguardStateCallback", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 019d428..8b93b17 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -68,6 +68,7 @@
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.SecureSettings;
@@ -150,7 +151,8 @@
@Main CoroutineDispatcher mainDispatcher,
Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel,
SystemPropertiesHelper systemPropertiesHelper,
- Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager) {
+ Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager,
+ SelectedUserInteractor selectedUserInteractor) {
return new KeyguardViewMediator(
context,
uiEventLogger,
@@ -194,7 +196,8 @@
mainDispatcher,
dreamingToLockscreenTransitionViewModel,
systemPropertiesHelper,
- wmLockscreenVisibilityManager);
+ wmLockscreenVisibilityManager,
+ selectedUserInteractor);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index a4a3126..2dc4908 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -31,7 +31,6 @@
import com.android.systemui.doze.DozeTransitionCallback
import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.dreams.DreamOverlayCallbackController
-import com.android.systemui.keyguard.ScreenLifecycle
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DismissAction
@@ -41,9 +40,6 @@
import com.android.systemui.keyguard.shared.model.KeyguardRootViewVisibilityState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.phone.BiometricUnlockController
-import com.android.systemui.statusbar.phone.BiometricUnlockController.WakeAndUnlockMode
-import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -111,6 +107,8 @@
/** Is the always-on display available to be used? */
val isAodAvailable: Flow<Boolean>
+ fun setAodAvailable(value: Boolean)
+
/**
* Observable for whether we are in doze state.
*
@@ -160,6 +158,8 @@
/** Observable for biometric unlock modes */
val biometricUnlockState: Flow<BiometricUnlockModel>
+ fun setBiometricUnlockState(value: BiometricUnlockModel)
+
/** Approximate location on the screen of the fingerprint sensor. */
val fingerprintSensorLocation: Flow<Point?>
@@ -240,12 +240,9 @@
@Inject
constructor(
statusBarStateController: StatusBarStateController,
- screenLifecycle: ScreenLifecycle,
- biometricUnlockController: BiometricUnlockController,
private val keyguardStateController: KeyguardStateController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val dozeTransitionListener: DozeTransitionListener,
- private val dozeParameters: DozeParameters,
private val authController: AuthController,
private val dreamOverlayCallbackController: DreamOverlayCallbackController,
@Main private val mainDispatcher: CoroutineDispatcher,
@@ -303,24 +300,12 @@
}
.distinctUntilChanged()
- override val isAodAvailable: Flow<Boolean> =
- conflatedCallbackFlow {
- val callback =
- DozeParameters.Callback {
- trySendWithFailureLogging(
- dozeParameters.alwaysOn,
- TAG,
- "updated isAodAvailable"
- )
- }
+ private val _isAodAvailable = MutableStateFlow(false)
+ override val isAodAvailable: Flow<Boolean> = _isAodAvailable.asStateFlow()
- dozeParameters.addCallback(callback)
- // Adding the callback does not send an initial update.
- trySendWithFailureLogging(dozeParameters.alwaysOn, TAG, "initial isAodAvailable")
-
- awaitClose { dozeParameters.removeCallback(callback) }
- }
- .distinctUntilChanged()
+ override fun setAodAvailable(value: Boolean) {
+ _isAodAvailable.value = value
+ }
override val isKeyguardOccluded: Flow<Boolean> =
conflatedCallbackFlow {
@@ -506,30 +491,11 @@
statusBarStateIntToObject(statusBarStateController.state)
)
- override val biometricUnlockState: Flow<BiometricUnlockModel> = conflatedCallbackFlow {
- fun dispatchUpdate() {
- trySendWithFailureLogging(
- biometricModeIntToObject(biometricUnlockController.mode),
- TAG,
- "biometric mode"
- )
- }
+ private val _biometricUnlockState = MutableStateFlow(BiometricUnlockModel.NONE)
+ override val biometricUnlockState = _biometricUnlockState.asStateFlow()
- val callback =
- object : BiometricUnlockController.BiometricUnlockEventsListener {
- override fun onModeChanged(@WakeAndUnlockMode mode: Int) {
- dispatchUpdate()
- }
-
- override fun onResetMode() {
- dispatchUpdate()
- }
- }
-
- biometricUnlockController.addListener(callback)
- dispatchUpdate()
-
- awaitClose { biometricUnlockController.removeListener(callback) }
+ override fun setBiometricUnlockState(value: BiometricUnlockModel) {
+ _biometricUnlockState.value = value
}
override val fingerprintSensorLocation: Flow<Point?> = conflatedCallbackFlow {
@@ -662,20 +628,6 @@
}
}
- private fun biometricModeIntToObject(@WakeAndUnlockMode value: Int): BiometricUnlockModel {
- return when (value) {
- 0 -> BiometricUnlockModel.NONE
- 1 -> BiometricUnlockModel.WAKE_AND_UNLOCK
- 2 -> BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING
- 3 -> BiometricUnlockModel.SHOW_BOUNCER
- 4 -> BiometricUnlockModel.ONLY_WAKE
- 5 -> BiometricUnlockModel.UNLOCK_COLLAPSING
- 6 -> BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
- 7 -> BiometricUnlockModel.DISMISS_BOUNCER
- else -> throw IllegalArgumentException("Invalid BiometricUnlockModel value: $value")
- }
- }
-
private fun dozeMachineStateToModel(state: DozeMachine.State): DozeStateModel {
return when (state) {
DozeMachine.State.UNINITIALIZED -> DozeStateModel.UNINITIALIZED
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index 84cd3ef..3eef6aa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -25,6 +25,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import java.util.UUID
@@ -48,8 +49,8 @@
* [TransitionInteractor]. These interactors will call [startTransition] and [updateTransition] on
* this repository.
*
- * To print all transitions to logcat to help with debugging, run this command:
- * adb shell settings put global systemui/buffer/KeyguardLog VERBOSE
+ * To print all transitions to logcat to help with debugging, run this command: adb shell settings
+ * put global systemui/buffer/KeyguardLog VERBOSE
*
* This will print all keyguard transitions to logcat with the KeyguardTransitionAuditLogger tag.
*/
@@ -73,11 +74,8 @@
/**
* Begin a transition from one state to another. Transitions are interruptible, and will issue a
* [TransitionStep] with state = [TransitionState.CANCELED] before beginning the next one.
- *
- * When canceled, there are two options: to continue from the current position of the prior
- * transition, or to reset the position. When [resetIfCanceled] == true, it will do the latter.
*/
- fun startTransition(info: TransitionInfo, resetIfCanceled: Boolean = false): UUID?
+ fun startTransition(info: TransitionInfo): UUID?
/**
* Allows manual control of a transition. When calling [startTransition], the consumer must pass
@@ -138,10 +136,7 @@
)
}
- override fun startTransition(
- info: TransitionInfo,
- resetIfCanceled: Boolean,
- ): UUID? {
+ override fun startTransition(info: TransitionInfo): UUID? {
if (lastStep.from == info.from && lastStep.to == info.to) {
Log.i(TAG, "Duplicate call to start the transition, rejecting: $info")
return null
@@ -149,10 +144,10 @@
val startingValue =
if (lastStep.transitionState != TransitionState.FINISHED) {
Log.i(TAG, "Transition still active: $lastStep, canceling")
- if (resetIfCanceled) {
- 0f
- } else {
- lastStep.value
+ when (info.modeOnCanceled) {
+ TransitionModeOnCanceled.LAST_VALUE -> lastStep.value
+ TransitionModeOnCanceled.RESET -> 0f
+ TransitionModeOnCanceled.REVERSE -> 1f - lastStep.value
}
} else {
0f
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricUnlockInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricUnlockInteractor.kt
new file mode 100644
index 0000000..cb003a7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricUnlockInteractor.kt
@@ -0,0 +1,42 @@
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_DISMISS_BOUNCER
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_NONE
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_ONLY_WAKE
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_SHOW_BOUNCER
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_UNLOCK_COLLAPSING
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+import com.android.systemui.statusbar.phone.BiometricUnlockController.WakeAndUnlockMode
+import javax.inject.Inject
+
+@SysUISingleton
+class BiometricUnlockInteractor
+@Inject
+constructor(
+ private val keyguardRepository: KeyguardRepository,
+) {
+
+ fun setBiometricUnlockState(@WakeAndUnlockMode unlockStateInt: Int) {
+ val state = biometricModeIntToObject(unlockStateInt)
+ keyguardRepository.setBiometricUnlockState(state)
+ }
+
+ private fun biometricModeIntToObject(@WakeAndUnlockMode value: Int): BiometricUnlockModel {
+ return when (value) {
+ MODE_NONE -> BiometricUnlockModel.NONE
+ MODE_WAKE_AND_UNLOCK -> BiometricUnlockModel.WAKE_AND_UNLOCK
+ MODE_WAKE_AND_UNLOCK_PULSING -> BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING
+ MODE_SHOW_BOUNCER -> BiometricUnlockModel.SHOW_BOUNCER
+ MODE_ONLY_WAKE -> BiometricUnlockModel.ONLY_WAKE
+ MODE_UNLOCK_COLLAPSING -> BiometricUnlockModel.UNLOCK_COLLAPSING
+ MODE_WAKE_AND_UNLOCK_FROM_DREAM -> BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
+ MODE_DISMISS_BOUNCER -> BiometricUnlockModel.DISMISS_BOUNCER
+ else -> throw IllegalArgumentException("Invalid BiometricUnlockModel value: $value")
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
index 0c898be..af1802f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
@@ -28,6 +28,10 @@
private val keyguardRepository: KeyguardRepository,
) {
+ fun setAodAvailable(value: Boolean) {
+ keyguardRepository.setAodAvailable(value)
+ }
+
fun setIsDozing(isDozing: Boolean) {
keyguardRepository.setIsDozing(isDozing)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index 6e0aa4c..a331a66 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -24,6 +24,7 @@
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion.isWakeAndUnlock
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
@@ -65,8 +66,21 @@
)
.collect { (_, lastStartedStep, occluded) ->
if (lastStartedStep.to == KeyguardState.AOD) {
- startTransitionTo(
+ val toState =
if (occluded) KeyguardState.OCCLUDED else KeyguardState.LOCKSCREEN
+ val modeOnCanceled =
+ if (
+ toState == KeyguardState.LOCKSCREEN &&
+ lastStartedStep.from == KeyguardState.LOCKSCREEN
+ ) {
+ TransitionModeOnCanceled.REVERSE
+ } else {
+ TransitionModeOnCanceled.LAST_VALUE
+ }
+
+ startTransitionTo(
+ toState = toState,
+ modeOnCanceled = modeOnCanceled,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index c67153a..eace0c7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -22,6 +22,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
@@ -114,8 +115,9 @@
.collect { (isAsleep, lastStartedStep, isAodAvailable) ->
if (lastStartedStep.to == KeyguardState.GONE && isAsleep) {
startTransitionTo(
- if (isAodAvailable) KeyguardState.AOD else KeyguardState.DOZING,
- resetIfCancelled = true,
+ toState =
+ if (isAodAvailable) KeyguardState.AOD else KeyguardState.DOZING,
+ modeOnCanceled = TransitionModeOnCanceled.RESET,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index c39a4c9..d44a9d8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -27,6 +27,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD
import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.shade.data.repository.ShadeRepository
@@ -340,8 +341,20 @@
)
.collect { (isAsleep, lastStartedStep, isAodAvailable) ->
if (lastStartedStep.to == KeyguardState.LOCKSCREEN && isAsleep) {
- startTransitionTo(
+ val toState =
if (isAodAvailable) KeyguardState.AOD else KeyguardState.DOZING
+ val modeOnCanceled =
+ if (
+ toState == KeyguardState.AOD &&
+ lastStartedStep.from == KeyguardState.AOD
+ ) {
+ TransitionModeOnCanceled.REVERSE
+ } else {
+ TransitionModeOnCanceled.LAST_VALUE
+ }
+ startTransitionTo(
+ toState = toState,
+ modeOnCanceled = modeOnCanceled,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index ad2ec69..24b6661 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -18,7 +18,6 @@
import android.animation.ValueAnimator
import com.android.keyguard.KeyguardSecurityModel
-import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
@@ -26,20 +25,22 @@
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
+import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
import com.android.systemui.util.kotlin.Utils.Companion.toQuint
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import com.android.wm.shell.animation.Interpolators
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
-import javax.inject.Inject
-import kotlin.time.Duration.Companion.milliseconds
@SysUISingleton
class FromPrimaryBouncerTransitionInteractor
@@ -51,6 +52,7 @@
private val keyguardInteractor: KeyguardInteractor,
private val flags: FeatureFlags,
private val keyguardSecurityModel: KeyguardSecurityModel,
+ private val selectedUserInteractor: SelectedUserInteractor,
private val powerInteractor: PowerInteractor,
) :
TransitionInteractor(
@@ -132,7 +134,7 @@
.collect {
(
isBouncerShowing,
- isAwake,
+ isAwake,
lastStartedTransitionStep,
occluded,
isActiveDreamLockscreenHosted) ->
@@ -162,8 +164,7 @@
),
::toQuad
)
- .collect {
- (isBouncerShowing, isAsleep, lastStartedTransitionStep, isAodAvailable)
+ .collect { (isBouncerShowing, isAsleep, lastStartedTransitionStep, isAodAvailable)
->
if (
!isBouncerShowing &&
@@ -181,25 +182,24 @@
private fun listenForPrimaryBouncerToDreamingLockscreenHosted() {
scope.launch {
keyguardInteractor.primaryBouncerShowing
- .sample(
- combine(
- keyguardInteractor.isActiveDreamLockscreenHosted,
- transitionInteractor.startedKeyguardTransitionStep,
- ::Pair
- ),
- ::toTriple
- )
- .collect { (isBouncerShowing,
- isActiveDreamLockscreenHosted,
- lastStartedTransitionStep) ->
- if (
- !isBouncerShowing &&
- isActiveDreamLockscreenHosted &&
- lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER
- ) {
- startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
- }
+ .sample(
+ combine(
+ keyguardInteractor.isActiveDreamLockscreenHosted,
+ transitionInteractor.startedKeyguardTransitionStep,
+ ::Pair
+ ),
+ ::toTriple
+ )
+ .collect {
+ (isBouncerShowing, isActiveDreamLockscreenHosted, lastStartedTransitionStep) ->
+ if (
+ !isBouncerShowing &&
+ isActiveDreamLockscreenHosted &&
+ lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER
+ ) {
+ startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
}
+ }
}
}
@@ -221,7 +221,7 @@
) {
val securityMode =
keyguardSecurityModel.getSecurityMode(
- KeyguardUpdateMonitor.getCurrentUser()
+ selectedUserInteractor.getSelectedUserId()
)
// IME for password requires a slightly faster animation
val duration =
@@ -237,7 +237,7 @@
getDefaultAnimatorForTransitionsToState(KeyguardState.GONE).apply {
this.duration = duration.inWholeMilliseconds
},
- resetIfCancelled = true
+ modeOnCanceled = TransitionModeOnCanceled.RESET,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
index cab6928..628e912 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
@@ -24,7 +24,7 @@
import com.android.systemui.keyguard.shared.model.DismissAction
import com.android.systemui.keyguard.shared.model.KeyguardDone
import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
@@ -40,11 +40,11 @@
@Inject
constructor(
trustRepository: TrustRepository,
- val keyguardRepository: KeyguardRepository,
- val primaryBouncerInteractor: PrimaryBouncerInteractor,
- val alternateBouncerInteractor: AlternateBouncerInteractor,
- val powerInteractor: PowerInteractor,
- val userInteractor: UserInteractor,
+ private val keyguardRepository: KeyguardRepository,
+ primaryBouncerInteractor: PrimaryBouncerInteractor,
+ alternateBouncerInteractor: AlternateBouncerInteractor,
+ powerInteractor: PowerInteractor,
+ private val selectedUserInteractor: SelectedUserInteractor,
) {
/*
* Updates when a biometric has authenticated the device and is requesting to dismiss
@@ -82,7 +82,7 @@
*/
private val primaryAuthenticated: Flow<Unit> =
primaryBouncerInteractor.keyguardAuthenticatedPrimaryAuth
- .filter { authedUserId -> authedUserId == userInteractor.getSelectedUserId() }
+ .filter { authedUserId -> authedUserId == selectedUserInteractor.getSelectedUserId() }
.map {} // map to Unit
/*
@@ -92,7 +92,7 @@
*/
private val userRequestedBouncerWhenAlreadyAuthenticated: Flow<Unit> =
primaryBouncerInteractor.userRequestedBouncerWhenAlreadyAuthenticated
- .filter { authedUserId -> authedUserId == userInteractor.getSelectedUserId() }
+ .filter { authedUserId -> authedUserId == selectedUserInteractor.getSelectedUserId() }
.map {} // map to Unit
/** Updates when keyguardDone should be requested. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index 54c6d5f..7601808 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -21,6 +21,7 @@
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.util.kotlin.sample
import java.util.UUID
import kotlinx.coroutines.CoroutineScope
@@ -49,7 +50,7 @@
fun startTransitionTo(
toState: KeyguardState,
animator: ValueAnimator? = getDefaultAnimatorForTransitionsToState(toState),
- resetIfCancelled: Boolean = false,
+ modeOnCanceled: TransitionModeOnCanceled = TransitionModeOnCanceled.LAST_VALUE
): UUID? {
if (
fromState != transitionInteractor.startedKeyguardState.value &&
@@ -73,8 +74,8 @@
fromState,
toState,
animator,
- ),
- resetIfCancelled
+ modeOnCanceled,
+ )
)
}
@@ -91,8 +92,8 @@
// so use the last finishedKeyguardState to determine the overriding FROM state
if (finishedKeyguardState == fromState) {
startTransitionTo(
- KeyguardState.OCCLUDED,
- resetIfCancelled = true,
+ toState = KeyguardState.OCCLUDED,
+ modeOnCanceled = TransitionModeOnCanceled.RESET,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionInfo.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionInfo.kt
index bfccf3fe..7a37365 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionInfo.kt
@@ -22,7 +22,13 @@
val ownerName: String,
val from: KeyguardState,
val to: KeyguardState,
- val animator: ValueAnimator?, // 'null' animator signal manual control
+ /** [null] animator signals manual control, otherwise transition run by the animator */
+ val animator: ValueAnimator?,
+ /**
+ * If the transition resets in the cancellation of another transition, use this mode to
+ * determine how to continue.
+ */
+ val modeOnCanceled: TransitionModeOnCanceled = TransitionModeOnCanceled.LAST_VALUE,
) {
override fun toString(): String =
"TransitionInfo(ownerName=$ownerName, from=$from, to=$to, " +
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionModeOnCanceled.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionModeOnCanceled.kt
new file mode 100644
index 0000000..56f90bd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionModeOnCanceled.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.keyguard.shared.model
+
+/** When canceled, provide different ways to start the next transition. */
+enum class TransitionModeOnCanceled {
+ /** Proceed from the last value. If canceled at .7, start from .7 and end at 1 */
+ LAST_VALUE,
+ /** Start over from 0. If canceled at .7, start from 0 and end at 1 */
+ RESET,
+ /** Reverse the transition. If canceled at .7, start from 1-.7 (0.3) and end at 1 */
+ REVERSE
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt
index f14552b..87d8164 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt
@@ -25,20 +25,18 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissInteractor
import com.android.systemui.keyguard.shared.model.KeyguardDone
import com.android.systemui.log.core.LogLevel
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
/** Handles keyguard dismissal requests. */
-@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class KeyguardDismissBinder
@Inject
constructor(
private val interactor: KeyguardDismissInteractor,
- private val userInteractor: UserInteractor,
+ private val selectedUserInteractor: SelectedUserInteractor,
private val viewMediatorCallback: ViewMediatorCallback,
@Application private val scope: CoroutineScope,
private val keyguardLogger: KeyguardLogger,
@@ -55,11 +53,15 @@
when (keyguardDoneTiming) {
KeyguardDone.LATER -> {
log("keyguardDonePending")
- viewMediatorCallback.keyguardDonePending(userInteractor.getSelectedUserId())
+ viewMediatorCallback.keyguardDonePending(
+ selectedUserInteractor.getSelectedUserId()
+ )
}
else -> {
log("keyguardDone")
- viewMediatorCallback.keyguardDone(userInteractor.getSelectedUserId())
+ viewMediatorCallback.keyguardDone(
+ selectedUserInteractor.getSelectedUserId()
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
index 342a440..9371d4e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
@@ -50,8 +50,11 @@
override fun addViews(constraintLayout: ConstraintLayout) {
if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- LayoutInflater.from(constraintLayout.context)
- .inflate(R.layout.ambient_indication, constraintLayout, true)
+ val view =
+ LayoutInflater.from(constraintLayout.context)
+ .inflate(R.layout.ambient_indication, constraintLayout, false)
+
+ constraintLayout.addView(view)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 67531ad..fd6b3f1 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -190,17 +190,6 @@
}
}
- /**
- * Provides a logging buffer for all logs related to Quick Settings tiles. This LogBuffer is
- * unique for each tile.
- * go/qs-tile-refactor
- */
- @Provides
- @QSTilesDefaultLog
- public static LogBuffer provideQuickSettingsTilesLogBuffer(LogBufferFactory factory) {
- return factory.create("QSTileLog", 25 /* maxSize */, false /* systrace */);
- }
-
@Provides
@QSTilesLogBuffers
public static Map<TileSpec, LogBuffer> provideQuickSettingsTilesLogBufferCache() {
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/QSTilesDefaultLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/QSTilesDefaultLog.kt
deleted file mode 100644
index 6575cdd..0000000
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/QSTilesDefaultLog.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.log.dagger
-
-import javax.inject.Qualifier
-
-/**
- * A default [com.android.systemui.log.LogBuffer] for QS tiles messages. It's used exclusively in
- * [com.android.systemui.qs.tiles.base.logging.QSTileLogger]. If you need to increase it for you
- * tile, add one to the map provided by the [QSTilesLogBuffers]
- */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class QSTilesDefaultLog
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index a48e56a..7cb5b3b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -731,6 +731,7 @@
removePlayer(existingSmartspaceMediaKey, dismissMediaData = false)
removedPlayer?.run {
debugLogger.logPotentialMemoryLeak(existingSmartspaceMediaKey)
+ onDestroy()
}
}
@@ -1302,6 +1303,7 @@
val removedPlayer = removeMediaPlayer(key)
if (removedPlayer != null && removedPlayer != player) {
debugLogger?.logPotentialMemoryLeak(key)
+ removedPlayer.onDestroy()
}
val sortKey =
MediaSortKey(
@@ -1329,6 +1331,7 @@
val removedPlayer = removeMediaPlayer(key)
if (!update && removedPlayer != null && removedPlayer != player) {
debugLogger?.logPotentialMemoryLeak(key)
+ removedPlayer.onDestroy()
}
val sortKey =
MediaSortKey(
@@ -1357,7 +1360,10 @@
// MediaPlayer should not be visible
// no need to set isDismissed flag.
val removedPlayer = removeMediaPlayer(newKey)
- removedPlayer?.run { debugLogger?.logPotentialMemoryLeak(newKey) }
+ removedPlayer?.run {
+ debugLogger?.logPotentialMemoryLeak(newKey)
+ onDestroy()
+ }
mediaData.put(newKey, it)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
index a53f0f1..ce8b79c 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
@@ -24,6 +24,7 @@
import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_SYSTEM_UI_SCREEN_RECORDER as METRICS_CREATION_SOURCE_SYSTEM_UI_SCREEN_RECORDER
import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN as METRICS_CREATION_SOURCE_UNKNOWN
import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_PERMISSION_REQUEST_DISPLAYED as METRICS_STATE_PERMISSION_REQUEST_DISPLAYED
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
@@ -47,6 +48,10 @@
)
}
+ fun notifyPermissionRequestDisplayed() {
+ notifyToServer(METRICS_STATE_PERMISSION_REQUEST_DISPLAYED, SessionCreationSource.UNKNOWN)
+ }
+
/**
* Request to log that the permission request moved to the given state.
*
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index fa418fc..2d830d3 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -249,6 +249,10 @@
setUpDialog(mDialog);
mDialog.show();
+
+ if (savedInstanceState == null) {
+ mMediaProjectionMetricsLogger.notifyPermissionRequestDisplayed();
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
index c91ed13..8e30740 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
@@ -42,7 +42,7 @@
import com.android.systemui.security.data.repository.SecurityRepository
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.user.data.repository.UserSwitcherRepository
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
@@ -102,7 +102,7 @@
private val deviceProvisionedController: DeviceProvisionedController,
private val qsSecurityFooterUtils: QSSecurityFooterUtils,
private val fgsManagerController: FgsManagerController,
- private val userInteractor: UserInteractor,
+ private val userSwitcherInteractor: UserSwitcherInteractor,
securityRepository: SecurityRepository,
foregroundServicesRepository: ForegroundServicesRepository,
userSwitcherRepository: UserSwitcherRepository,
@@ -178,6 +178,6 @@
}
override fun showUserSwitcher(expandable: Expandable) {
- userInteractor.showUserSwitcher(expandable)
+ userSwitcherInteractor.showUserSwitcher(expandable)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 959afd8..e27a59c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -30,12 +30,12 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.res.R;
import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile;
@@ -45,6 +45,7 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.res.R;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -69,6 +70,7 @@
private final DialogLaunchAnimator mDialogLaunchAnimator;
private final FeatureFlags mFlags;
private final PanelInteractor mPanelInteractor;
+ private final MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
private long mMillisUntilFinished = 0;
@@ -88,7 +90,8 @@
KeyguardDismissUtil keyguardDismissUtil,
KeyguardStateController keyguardStateController,
DialogLaunchAnimator dialogLaunchAnimator,
- PanelInteractor panelInteractor
+ PanelInteractor panelInteractor,
+ MediaProjectionMetricsLogger mediaProjectionMetricsLogger
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
@@ -99,6 +102,7 @@
mKeyguardStateController = keyguardStateController;
mDialogLaunchAnimator = dialogLaunchAnimator;
mPanelInteractor = panelInteractor;
+ mMediaProjectionMetricsLogger = mediaProjectionMetricsLogger;
}
@Override
@@ -190,6 +194,9 @@
} else {
dialog.show();
}
+
+ mMediaProjectionMetricsLogger.notifyPermissionRequestDisplayed();
+
return false;
};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index 1b0d5f9..2f8fe42 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -32,7 +32,6 @@
import androidx.annotation.Nullable;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -43,6 +42,7 @@
import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -55,7 +55,7 @@
private final KeyguardStateController mKeyguard;
protected IndividualSensorPrivacyController mSensorPrivacyController;
- private final SafetyCenterManager mSafetyCenterManager;
+ private final Boolean mIsSafetyCenterEnabled;
/**
* @return Id of the sensor that will be toggled
@@ -89,7 +89,7 @@
statusBarStateController, activityStarter, qsLogger);
mSensorPrivacyController = sensorPrivacyController;
mKeyguard = keyguardStateController;
- mSafetyCenterManager = safetyCenterManager;
+ mIsSafetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabled();
mSensorPrivacyController.observe(getLifecycle(), this);
}
@@ -138,7 +138,7 @@
@Override
public Intent getLongClickIntent() {
- if (mSafetyCenterManager.isSafetyCenterEnabled()) {
+ if (mIsSafetyCenterEnabled) {
return new Intent(Settings.ACTION_PRIVACY_CONTROLS);
} else {
return new Intent(Settings.ACTION_PRIVACY_SETTINGS);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
index 70a683b..f8de365 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
@@ -19,8 +19,8 @@
import androidx.annotation.GuardedBy
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogBufferFactory
import com.android.systemui.log.core.LogLevel
-import com.android.systemui.log.dagger.QSTilesDefaultLog
import com.android.systemui.log.dagger.QSTilesLogBuffers
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -29,14 +29,13 @@
import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.statusbar.StatusBarState
import javax.inject.Inject
-import javax.inject.Provider
@SysUISingleton
class QSTileLogger
@Inject
constructor(
@QSTilesLogBuffers logBuffers: Map<TileSpec, LogBuffer>,
- @QSTilesDefaultLog private val defaultLogBufferProvider: Provider<LogBuffer>,
+ private val factory: LogBufferFactory,
private val mStatusBarStateController: StatusBarStateController,
) {
@GuardedBy("logBufferCache") private val logBufferCache = logBuffers.toMutableMap()
@@ -154,7 +153,13 @@
private fun TileSpec.getLogBuffer(): LogBuffer =
synchronized(logBufferCache) {
- logBufferCache.getOrPut(this) { defaultLogBufferProvider.get() }
+ logBufferCache.getOrPut(this) {
+ factory.create(
+ "QSTileLog_${this.getLogTag()}",
+ BUFFER_MAX_SIZE /* maxSize */,
+ false /* systrace */
+ )
+ }
}
private fun StateUpdateTrigger.toLogString(): String =
@@ -185,5 +190,6 @@
private companion object {
const val TAG_FORMAT_PREFIX = "QSLog"
const val DATA_MAX_LENGTH = 50
+ const val BUFFER_MAX_SIZE = 25
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index cf1fbe3..d6f1ed9 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -68,6 +68,10 @@
*/
@WeaklyReferencedCallback
interface Callback {
+ /**
+ * Notifies that the current user will be changed.
+ */
+ fun onBeforeUserSwitching(newUser: Int) {}
/**
* Same as {@link onUserChanging(Int, Context, Runnable)} but the callback will be
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 393a698..9f416bb 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -90,6 +90,7 @@
private val isBackgroundUserSwitchEnabled: Boolean get() =
featureFlagsProvider.get().isEnabled(Flags.USER_TRACKER_BACKGROUND_CALLBACKS)
+ @Deprecated("Use UserInteractor.getSelectedUserId()")
override var userId: Int by SynchronizedDelegate(context.userId)
protected set
@@ -226,6 +227,13 @@
protected open fun handleBeforeUserSwitching(newUserId: Int) {
Assert.isNotMainThread()
setUserIdInternal(newUserId)
+
+ val list = synchronized(callbacks) {
+ callbacks.toList()
+ }
+ list.forEach {
+ it.callback.get()?.onBeforeUserSwitching(newUserId)
+ }
}
@WorkerThread
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 6f5e41f..0426388 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -47,7 +47,6 @@
import android.view.WindowManagerGlobal;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -72,6 +71,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import dagger.Lazy;
@@ -112,6 +112,7 @@
private final KeyguardBypassController mKeyguardBypassController;
private final Executor mBackgroundExecutor;
private final AuthController mAuthController;
+ private final Lazy<SelectedUserInteractor> mUserInteractor;
private final Lazy<ShadeInteractor> mShadeInteractorLazy;
private ViewGroup mWindowRootView;
private LayoutParams mLp;
@@ -157,7 +158,8 @@
AuthController authController,
ShadeExpansionStateManager shadeExpansionStateManager,
Lazy<ShadeInteractor> shadeInteractorLazy,
- ShadeWindowLogger logger) {
+ ShadeWindowLogger logger,
+ Lazy<SelectedUserInteractor> userInteractor) {
mContext = context;
mWindowRootViewComponentFactory = windowRootViewComponentFactory;
mWindowManager = windowManager;
@@ -174,6 +176,7 @@
mScreenOffAnimationController = screenOffAnimationController;
dumpManager.registerDumpable(this);
mAuthController = authController;
+ mUserInteractor = userInteractor;
mLastKeyguardRotationAllowed = mKeyguardStateController.isKeyguardScreenRotationAllowed();
mLockScreenDisplayTimeout = context.getResources()
.getInteger(R.integer.config_lockScreenDisplayTimeout);
@@ -348,7 +351,7 @@
boolean onKeyguard = state.statusBarState == StatusBarState.KEYGUARD
&& !state.keyguardFadingAway && !state.keyguardGoingAway;
if (onKeyguard
- && mAuthController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())) {
+ && mAuthController.isUdfpsEnrolled(mUserInteractor.get().getSelectedUserId())) {
// both max and min display refresh rate must be set to take effect:
mLpChanged.preferredMaxDisplayRefreshRate = mKeyguardPreferredRefreshRate;
mLpChanged.preferredMinDisplayRefreshRate = mKeyguardPreferredRefreshRate;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index d05dfe2..d869239 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -35,18 +35,20 @@
import com.android.keyguard.LockIconViewController;
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.Dumpable;
+import com.android.systemui.FeatureFlags;
import com.android.systemui.animation.ActivityLaunchAnimator;
-import com.android.systemui.back.domain.interactor.BackActionInteractor;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.bouncer.ui.binder.KeyguardBouncerViewBinder;
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel;
+import com.android.systemui.compose.ComposeFacade;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
@@ -55,7 +57,6 @@
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.log.BouncerLogger;
-import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.res.R;
import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener;
import com.android.systemui.statusbar.DragDownHelper;
@@ -75,6 +76,7 @@
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.time.SystemClock;
import java.io.PrintWriter;
@@ -104,8 +106,10 @@
private final PulsingGestureListener mPulsingGestureListener;
private final LockscreenHostedDreamGestureListener mLockscreenHostedDreamGestureListener;
private final NotificationInsetsController mNotificationInsetsController;
+ private final CommunalViewModel mCommunalViewModel;
private final boolean mIsTrackpadCommonEnabled;
private final FeatureFlags mFeatureFlags;
+ private final FeatureFlagsClassic mFeatureFlagsClassic;
private final SysUIKeyEventHandler mSysUIKeyEventHandler;
private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
private final AlternateBouncerInteractor mAlternateBouncerInteractor;
@@ -128,8 +132,6 @@
private final CentralSurfaces mService;
private final DozeServiceHost mDozeServiceHost;
private final DozeScrimController mDozeScrimController;
- private final BackActionInteractor mBackActionInteractor;
- private final PowerInteractor mPowerInteractor;
private final NotificationShadeWindowController mNotificationShadeWindowController;
private DragDownHelper mDragDownHelper;
private boolean mExpandingBelowNotch;
@@ -164,8 +166,6 @@
CentralSurfaces centralSurfaces,
DozeServiceHost dozeServiceHost,
DozeScrimController dozeScrimController,
- BackActionInteractor backActionInteractor,
- PowerInteractor powerInteractor,
NotificationShadeWindowController controller,
Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
@@ -180,14 +180,17 @@
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
KeyguardTransitionInteractor keyguardTransitionInteractor,
PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
+ CommunalViewModel communalViewModel,
NotificationExpansionRepository notificationExpansionRepository,
+ FeatureFlagsClassic featureFlagsClassic,
FeatureFlags featureFlags,
SystemClock clock,
BouncerMessageInteractor bouncerMessageInteractor,
BouncerLogger bouncerLogger,
SysUIKeyEventHandler sysUIKeyEventHandler,
PrimaryBouncerInteractor primaryBouncerInteractor,
- AlternateBouncerInteractor alternateBouncerInteractor) {
+ AlternateBouncerInteractor alternateBouncerInteractor,
+ SelectedUserInteractor selectedUserInteractor) {
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
mStatusBarStateController = statusBarStateController;
@@ -200,20 +203,20 @@
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mStatusBarWindowStateController = statusBarWindowStateController;
mLockIconViewController = lockIconViewController;
- mBackActionInteractor = backActionInteractor;
mShadeLogger = shadeLogger;
mService = centralSurfaces;
mDozeServiceHost = dozeServiceHost;
mDozeScrimController = dozeScrimController;
- mPowerInteractor = powerInteractor;
mNotificationShadeWindowController = controller;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
mAmbientState = ambientState;
mPulsingGestureListener = pulsingGestureListener;
mLockscreenHostedDreamGestureListener = lockscreenHostedDreamGestureListener;
mNotificationInsetsController = notificationInsetsController;
- mIsTrackpadCommonEnabled = featureFlags.isEnabled(TRACKPAD_GESTURE_COMMON);
+ mCommunalViewModel = communalViewModel;
+ mIsTrackpadCommonEnabled = featureFlagsClassic.isEnabled(TRACKPAD_GESTURE_COMMON);
mFeatureFlags = featureFlags;
+ mFeatureFlagsClassic = featureFlagsClassic;
mSysUIKeyEventHandler = sysUIKeyEventHandler;
mPrimaryBouncerInteractor = primaryBouncerInteractor;
mAlternateBouncerInteractor = alternateBouncerInteractor;
@@ -229,7 +232,8 @@
messageAreaControllerFactory,
bouncerMessageInteractor,
bouncerLogger,
- featureFlags);
+ featureFlagsClassic,
+ selectedUserInteractor);
collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),
mLockscreenToDreamingTransition);
@@ -239,7 +243,7 @@
this::setExpandAnimationRunning);
mClock = clock;
- if (featureFlags.isEnabled(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION)) {
+ if (featureFlagsClassic.isEnabled(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION)) {
unfoldTransitionProgressProvider.ifPresent(
progressProvider -> progressProvider.addCallback(
mDisableSubpixelTextTransitionListener));
@@ -268,7 +272,7 @@
mStackScrollLayout = mView.findViewById(R.id.notification_stack_scroller);
mPulsingWakeupGestureHandler = new GestureDetector(mView.getContext(),
mPulsingGestureListener);
- if (mFeatureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) {
+ if (mFeatureFlagsClassic.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) {
mDreamingWakeupGestureHandler = new GestureDetector(mView.getContext(),
mLockscreenHostedDreamGestureListener);
}
@@ -440,7 +444,7 @@
}
boolean bouncerShowing;
- if (mFeatureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+ if (mFeatureFlagsClassic.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing()
|| mAlternateBouncerInteractor.isVisibleState();
} else {
@@ -452,7 +456,7 @@
if (mDragDownHelper.isDragDownEnabled()) {
// This handles drag down over lockscreen
boolean result = mDragDownHelper.onInterceptTouchEvent(ev);
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (mFeatureFlagsClassic.isEnabled(Flags.MIGRATE_NSSL)) {
if (result) {
mLastInterceptWasDragDownHelper = true;
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
@@ -484,7 +488,7 @@
MotionEvent cancellation = MotionEvent.obtain(ev);
cancellation.setAction(MotionEvent.ACTION_CANCEL);
mStackScrollLayout.onInterceptTouchEvent(cancellation);
- if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (!mFeatureFlagsClassic.isEnabled(Flags.MIGRATE_NSSL)) {
mNotificationPanelViewController.handleExternalInterceptTouch(cancellation);
}
cancellation.recycle();
@@ -499,7 +503,7 @@
if (mStatusBarKeyguardViewManager.onTouch(ev)) {
return true;
}
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (mFeatureFlagsClassic.isEnabled(Flags.MIGRATE_NSSL)) {
if (mLastInterceptWasDragDownHelper && (mDragDownHelper.isDraggingDown())) {
// we still want to finish our drag down gesture when locking the screen
handled |= mDragDownHelper.onTouchEvent(ev) || handled;
@@ -564,8 +568,27 @@
mDepthController.onPanelExpansionChanged(currentState);
}
+ /**
+ * Sets up the communal hub UI if the {@link com.android.systemui.Flags#FLAG_COMMUNAL_HUB} flag
+ * is enabled.
+ *
+ * The layout lives in {@link R.id.communal_ui_container}.
+ */
+ public void setupCommunalHubLayout() {
+ if (!mFeatureFlags.communalHub() || !ComposeFacade.INSTANCE.isComposeAvailable()) {
+ return;
+ }
+
+ // Replace the placeholder view with the communal UI.
+ View communalPlaceholder = mView.findViewById(R.id.communal_ui_stub);
+ int index = mView.indexOfChild(communalPlaceholder);
+ mView.removeView(communalPlaceholder);
+ mView.addView(ComposeFacade.INSTANCE.createCommunalContainer(mView.getContext(),
+ mCommunalViewModel), index);
+ }
+
private boolean didNotificationPanelInterceptEvent(MotionEvent ev) {
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ if (mFeatureFlagsClassic.isEnabled(Flags.MIGRATE_NSSL)) {
// Since NotificationStackScrollLayout is now a sibling of notification_panel, we need
// to also ask NotificationPanelViewController directly, in order to process swipe up
// events originating from notifications
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index 3bbb2cf..3c68438 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -736,7 +736,11 @@
/** Returns whether touches from the notification panel should be disallowed */
public boolean disallowTouches() {
- return mQs.disallowPanelTouches();
+ if (mQs != null) {
+ return mQs.disallowPanelTouches();
+ } else {
+ return false;
+ }
}
void setListening(boolean listening) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index e487a6f..2f68476 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -34,8 +34,7 @@
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepository
-import com.android.systemui.user.domain.interactor.UserInteractor
-import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import javax.inject.Inject
import javax.inject.Provider
import kotlinx.coroutines.CoroutineScope
@@ -71,7 +70,7 @@
keyguardTransitionInteractor: KeyguardTransitionInteractor,
powerInteractor: PowerInteractor,
userSetupRepository: UserSetupRepository,
- userInteractor: UserInteractor,
+ userSwitcherInteractor: UserSwitcherInteractor,
sharedNotificationContainerInteractor: SharedNotificationContainerInteractor,
repository: ShadeRepository,
) {
@@ -140,13 +139,6 @@
/** Whether either the shade or QS is fully expanded. */
val isAnyFullyExpanded: Flow<Boolean> = anyExpansion.map { it >= 1f }.distinctUntilChanged()
- /** Whether either the shade or QS is expanding from a fully collapsed state. */
- val isAnyExpanding: Flow<Boolean> =
- anyExpansion
- .pairwise(1f)
- .map { (prev, curr) -> curr > 0f && curr < 1f && prev < 1f }
- .distinctUntilChanged()
-
/**
* Whether either the shade or QS is partially or fully expanded, i.e. not fully collapsed. At
* this time, this is not simply a matter of checking if either value in shadeExpansion and
@@ -227,7 +219,7 @@
isDeviceProvisioned &&
// Disallow QS during setup if it's a simple user switcher. (The user intends to
// use the lock screen user switcher, QS is not needed.)
- (isUserSetup || !userInteractor.isSimpleUserSwitcher) &&
+ (isUserSetup || !userSwitcherInteractor.isSimpleUserSwitcher) &&
isShadeEnabled &&
disableFlags.isQuickSettingsEnabled() &&
!isDozing
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index 17da015..ffde8c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -118,16 +118,27 @@
// If the view is being removed, this may be called even though we're not active
boolean remoteInputActiveForEntry = isRemoteInputActive(entry);
+ boolean remoteInputActive = isRemoteInputActive();
mLogger.logRemoveRemoteInput(
entry.getKey() /* entryKey */,
entry.mRemoteEditImeVisible /* remoteEditImeVisible */,
entry.mRemoteEditImeAnimatingAway /* remoteEditImeAnimatingAway */,
remoteInputActiveForEntry /* isRemoteInputActiveForEntry */,
- isRemoteInputActive()/* isRemoteInputActive */,
+ remoteInputActive/* isRemoteInputActive */,
reason/* reason */,
entry.getNotificationStyle()/* notificationStyle */);
- if (!remoteInputActiveForEntry) return;
+ if (!remoteInputActiveForEntry) {
+ if (mLastAppliedRemoteInputActive != null
+ && mLastAppliedRemoteInputActive
+ && !remoteInputActive) {
+ mLogger.logRemoteInputApplySkipped(
+ entry.getKey() /* entryKey */,
+ reason/* reason */,
+ entry.getNotificationStyle()/* notificationStyle */);
+ }
+ return;
+ }
pruneWeakThenRemoveAndContains(null /* contains */, entry /* remove */, token);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
index 57d20246..8957f29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
@@ -114,18 +114,21 @@
|| previousAnimator.getAnimatedFraction() == 0)) {
animator.setStartDelay(properties.delay);
}
- if (listener != null) {
- animator.addListener(listener);
- }
// remove the tag when the animation is finished
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- view.setTag(animatorTag, null);
- view.setTag(animationStartTag, null);
- view.setTag(animationEndTag, null);
+ Animator existing = (Animator) view.getTag(animatorTag);
+ if (existing == animation) {
+ view.setTag(animatorTag, null);
+ view.setTag(animationStartTag, null);
+ view.setTag(animationEndTag, null);
+ }
}
});
+ if (listener != null) {
+ animator.addListener(listener);
+ }
ViewState.startAnimator(animator, listener);
view.setTag(animatorTag, animator);
view.setTag(animationStartTag, currentValue);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt
index ff89c62..23f87ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt
@@ -83,6 +83,21 @@
}
)
+ fun logRemoteInputApplySkipped(entryKey: String, reason: String, notificationStyle: String) =
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = entryKey
+ str2 = reason
+ str3 = notificationStyle
+ },
+ {
+ "removeRemoteInput[apply is skipped] reason: $str2" +
+ "for entry: $str1, style: $str3 "
+ }
+ )
+
private companion object {
private const val TAG = "RemoteInputControllerLog"
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
index 9ba1f7a..380cdad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
@@ -30,6 +30,7 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import dagger.Binds
import dagger.Module
import javax.inject.Inject
@@ -52,7 +53,8 @@
private val lockscreenUserManager: NotificationLockscreenUserManager,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val statusBarStateController: StatusBarStateController,
- private val keyguardStateController: KeyguardStateController
+ private val keyguardStateController: KeyguardStateController,
+ private val selectedUserInteractor: SelectedUserInteractor,
) : Invalidator("SensitiveContentInvalidator"),
SensitiveContentCoordinator,
DynamicPrivacyController.Listener,
@@ -67,10 +69,10 @@
override fun onDynamicPrivacyChanged(): Unit = invalidateList("onDynamicPrivacyChanged")
override fun onBeforeRenderList(entries: List<ListEntry>) {
- if (keyguardStateController.isKeyguardGoingAway() ||
- statusBarStateController.getState() == StatusBarState.KEYGUARD &&
+ if (keyguardStateController.isKeyguardGoingAway ||
+ statusBarStateController.state == StatusBarState.KEYGUARD &&
keyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing(
- KeyguardUpdateMonitor.getCurrentUser())) {
+ selectedUserInteractor.getSelectedUserId())) {
// don't update yet if:
// - the keyguard is currently going away
// - LS is about to be dismissed by a biometric that bypasses LS (avoid notif flash)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index f750fed..1229cb9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -31,6 +31,7 @@
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
/** View-model for the shared notification container, used by both the shade and keyguard spaces */
class SharedNotificationContainerViewModel
@@ -45,8 +46,8 @@
) {
private val statesForConstrainedNotifications =
setOf(
- KeyguardState.LOCKSCREEN,
KeyguardState.AOD,
+ KeyguardState.LOCKSCREEN,
KeyguardState.DOZING,
KeyguardState.ALTERNATE_BOUNCER,
KeyguardState.PRIMARY_BOUNCER
@@ -68,8 +69,17 @@
/** If the user is visually on one of the unoccluded lockscreen states. */
val isOnLockscreen: Flow<Boolean> =
- keyguardTransitionInteractor.finishedKeyguardState
- .map { statesForConstrainedNotifications.contains(it) }
+ combine(
+ keyguardTransitionInteractor.finishedKeyguardState.map {
+ statesForConstrainedNotifications.contains(it)
+ },
+ keyguardTransitionInteractor
+ .transitionValue(KeyguardState.LOCKSCREEN)
+ .onStart { emit(0f) }
+ .map { it > 0 }
+ ) { constrainedNotificationState, transitioningToOrFromLockscreen ->
+ constrainedNotificationState || transitioningToOrFromLockscreen
+ }
.distinctUntilChanged()
/** Are we purely on the keyguard without the shade/qs? */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 8129b83..59f10ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -52,6 +52,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.keyguard.domain.interactor.BiometricUnlockInteractor;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
@@ -59,8 +60,11 @@
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.time.SystemClock;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -172,9 +176,11 @@
private final WakefulnessLifecycle mWakefulnessLifecycle;
private final LatencyTracker mLatencyTracker;
private final VibratorHelper mVibratorHelper;
+ private final BiometricUnlockInteractor mBiometricUnlockInteractor;
private final BiometricUnlockLogger mLogger;
private final SystemClock mSystemClock;
private final boolean mOrderUnlockAndWake;
+ private final Lazy<SelectedUserInteractor> mSelectedUserInteractor;
private final DeviceEntryHapticsInteractor mHapticsInteractor;
private long mLastFpFailureUptimeMillis;
@@ -286,7 +292,9 @@
VibratorHelper vibrator,
SystemClock systemClock,
FeatureFlags featureFlags,
- DeviceEntryHapticsInteractor hapticsInteractor
+ DeviceEntryHapticsInteractor hapticsInteractor,
+ Lazy<SelectedUserInteractor> selectedUserInteractor,
+ BiometricUnlockInteractor biometricUnlockInteractor
) {
mPowerManager = powerManager;
mUpdateMonitor = keyguardUpdateMonitor;
@@ -295,6 +303,7 @@
mLatencyTracker = latencyTracker;
mWakefulnessLifecycle = wakefulnessLifecycle;
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
+ mBiometricUnlockInteractor = biometricUnlockInteractor;
mNotificationShadeWindowController = notificationShadeWindowController;
mDozeScrimController = dozeScrimController;
@@ -317,6 +326,7 @@
mOrderUnlockAndWake = resources.getBoolean(
com.android.internal.R.bool.config_orderUnlockAndWake);
mHapticsInteractor = hapticsInteractor;
+ mSelectedUserInteractor = selectedUserInteractor;
dumpManager.registerDumpable(this);
}
@@ -525,6 +535,7 @@
for (BiometricUnlockEventsListener listener : mBiometricUnlockEventsListeners) {
listener.onModeChanged(mode);
}
+ mBiometricUnlockInteractor.setBiometricUnlockState(mode);
}
private void onBiometricUnlockedWithKeyguardDismissal(BiometricSourceType biometricSourceType) {
@@ -537,7 +548,8 @@
return mPendingAuthenticated != null
&& mUpdateMonitor
.isUnlockingWithBiometricAllowed(mPendingAuthenticated.isStrongBiometric)
- && mPendingAuthenticated.userId == KeyguardUpdateMonitor.getCurrentUser();
+ && mPendingAuthenticated.userId
+ == mSelectedUserInteractor.get().getSelectedUserId();
}
public @WakeAndUnlockMode int getMode() {
@@ -601,11 +613,11 @@
// if unlocking isn't allowed, log more information about why unlocking may not
// have been allowed
final int strongAuthFlags = mUpdateMonitor.getStrongAuthTracker().getStrongAuthForUser(
- KeyguardUpdateMonitor.getCurrentUser());
+ mSelectedUserInteractor.get().getSelectedUserId());
final boolean nonStrongBiometricAllowed =
mUpdateMonitor.getStrongAuthTracker()
.isNonStrongBiometricAllowedAfterIdleTimeout(
- KeyguardUpdateMonitor.getCurrentUser());
+ mSelectedUserInteractor.get().getSelectedUserId());
mLogger.logCalculateModeForFingerprintUnlockingNotAllowed(strongBiometric,
strongAuthFlags, nonStrongBiometricAllowed, deviceInteractive, keyguardShowing);
@@ -671,11 +683,11 @@
// if unlocking isn't allowed, log more information about why unlocking may not
// have been allowed
final int strongAuthFlags = mUpdateMonitor.getStrongAuthTracker().getStrongAuthForUser(
- KeyguardUpdateMonitor.getCurrentUser());
+ mSelectedUserInteractor.get().getSelectedUserId());
final boolean nonStrongBiometricAllowed =
mUpdateMonitor.getStrongAuthTracker()
.isNonStrongBiometricAllowedAfterIdleTimeout(
- KeyguardUpdateMonitor.getCurrentUser());
+ mSelectedUserInteractor.get().getSelectedUserId());
mLogger.logCalculateModeForPassiveAuthUnlockingNotAllowed(
strongBiometric, strongAuthFlags, nonStrongBiometricAllowed,
@@ -723,7 +735,7 @@
// Suppress all face auth errors if fingerprint can be used to authenticate
if ((biometricSourceType == BiometricSourceType.FACE
&& !mUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- KeyguardUpdateMonitor.getCurrentUser()))
+ mSelectedUserInteractor.get().getSelectedUserId()))
|| (biometricSourceType == BiometricSourceType.FINGERPRINT)) {
mHapticsInteractor.vibrateError();
}
@@ -781,6 +793,7 @@
for (BiometricUnlockEventsListener listener : mBiometricUnlockEventsListeners) {
listener.onResetMode();
}
+ mBiometricUnlockInteractor.setBiometricUnlockState(MODE_NONE);
mNumConsecutiveFpFailures = 0;
mLastFpFailureUptimeMillis = 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 9fb6c1b..8d9fd12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -1525,6 +1525,7 @@
// regressions, we'll continue standing up the root view in CentralSurfaces.
mNotificationShadeWindowController.fetchWindowRootView();
getNotificationShadeWindowViewController().setupExpandedStatusBar();
+ getNotificationShadeWindowViewController().setupCommunalHubLayout();
mShadeController.setNotificationShadeWindowViewController(
getNotificationShadeWindowViewController());
mBackActionInteractor.setup(mQsController, mShadeSurface);
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 7730f7d9..a11cbc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -37,14 +37,15 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -55,9 +56,7 @@
import com.android.systemui.unfold.SysUIUnfoldComponent;
import java.io.PrintWriter;
-import java.util.HashSet;
import java.util.Optional;
-import java.util.Set;
import javax.inject.Inject;
@@ -83,12 +82,11 @@
private final Resources mResources;
private final BatteryController mBatteryController;
private final ScreenOffAnimationController mScreenOffAnimationController;
+ private final DozeInteractor mDozeInteractor;
private final FoldAodAnimationController mFoldAodAnimationController;
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private final UserTracker mUserTracker;
- private final Set<Callback> mCallbacks = new HashSet<>();
-
private boolean mDozeAlwaysOn;
private boolean mControlScreenOffAnimation;
private boolean mIsQuickPickupEnabled;
@@ -131,7 +129,8 @@
KeyguardUpdateMonitor keyguardUpdateMonitor,
ConfigurationController configurationController,
StatusBarStateController statusBarStateController,
- UserTracker userTracker) {
+ UserTracker userTracker,
+ DozeInteractor dozeInteractor) {
mResources = resources;
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mAlwaysOnPolicy = alwaysOnDisplayPolicy;
@@ -144,6 +143,7 @@
mScreenOffAnimationController = screenOffAnimationController;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
mUserTracker = userTracker;
+ mDozeInteractor = dozeInteractor;
keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
tunerService.addTunable(
@@ -406,20 +406,6 @@
return mResources.getStringArray(R.array.doze_brightness_sensor_name_posture_mapping);
}
- /**
- * Callback to listen for DozeParameter changes.
- */
- public void addCallback(Callback callback) {
- mCallbacks.add(callback);
- }
-
- /**
- * Remove callback that listens for DozeParameter changes.
- */
- public void removeCallback(Callback callback) {
- mCallbacks.remove(callback);
- }
-
@Override
public void onTuningChanged(String key, String newValue) {
mDozeAlwaysOn = mAmbientDisplayConfiguration.alwaysOnEnabled(mUserTracker.getUserId());
@@ -465,10 +451,9 @@
}
private void dispatchAlwaysOnEvent() {
- for (Callback callback : mCallbacks) {
- callback.onAlwaysOnChange();
- }
mScreenOffAnimationController.onAlwaysOnChanged(getAlwaysOn());
+ mDozeInteractor.setAodAvailable(getAlwaysOn());
+
}
private boolean getPostureSpecificBool(
@@ -485,14 +470,6 @@
return bool;
}
- /** Callbacks for doze parameter related information */
- public interface Callback {
- /**
- * Invoked when the value of getAlwaysOn may have changed.
- */
- void onAlwaysOnChange();
- }
-
private final class SettingsObserver extends ContentObserver {
private final Uri mQuickPickupGesture =
Settings.Secure.getUriFor(Settings.Secure.DOZE_QUICK_PICKUP_GESTURE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index bde5c32..109e77e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -31,6 +31,7 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.Assert
import com.android.systemui.util.sensors.AsyncSensorManager
import java.io.PrintWriter
@@ -48,7 +49,8 @@
private val asyncSensorManager: AsyncSensorManager,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor,
- private val dumpManager: DumpManager
+ private val dumpManager: DumpManager,
+ private val selectedUserInteractor: SelectedUserInteractor,
) : Dumpable, CoreStartable {
private val pickupSensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE)
@@ -115,7 +117,7 @@
val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible &&
!statusBarStateController.isDozing
- val userId = KeyguardUpdateMonitor.getCurrentUser()
+ val userId = selectedUserInteractor.getSelectedUserId()
val isFaceEnabled = keyguardUpdateMonitor.isFaceAuthEnabledForUser(userId)
val shouldListen = (onKeyguard || bouncerVisible) && isFaceEnabled
if (shouldListen != isListening) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 400ac7b..90fddd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -92,6 +92,7 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.unfold.FoldAodAnimationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import dagger.Lazy;
@@ -313,6 +314,7 @@
private final KeyguardUpdateMonitor mKeyguardUpdateManager;
private final LatencyTracker mLatencyTracker;
private final KeyguardSecurityModel mKeyguardSecurityModel;
+ private final SelectedUserInteractor mSelectedUserInteractor;
@Nullable private KeyguardBypassController mBypassController;
@Nullable private OccludingAppBiometricUI mOccludingAppBiometricUI;
@@ -370,7 +372,8 @@
KeyguardTransitionInteractor keyguardTransitionInteractor,
@Main CoroutineDispatcher mainDispatcher,
Lazy<WindowManagerLockscreenVisibilityInteractor> wmLockscreenVisibilityInteractor,
- Lazy<KeyguardDismissActionInteractor> keyguardDismissActionInteractorLazy
+ Lazy<KeyguardDismissActionInteractor> keyguardDismissActionInteractorLazy,
+ SelectedUserInteractor selectedUserInteractor
) {
mContext = context;
mViewMediatorCallback = callback;
@@ -403,6 +406,7 @@
mMainDispatcher = mainDispatcher;
mWmLockscreenVisibilityInteractor = wmLockscreenVisibilityInteractor;
mKeyguardDismissActionInteractor = keyguardDismissActionInteractorLazy;
+ mSelectedUserInteractor = selectedUserInteractor;
}
KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@@ -1142,7 +1146,8 @@
*/
public boolean isSecure() {
return mKeyguardSecurityModel.getSecurityMode(
- KeyguardUpdateMonitor.getCurrentUser()) != KeyguardSecurityModel.SecurityMode.None;
+ mSelectedUserInteractor.getSelectedUserId())
+ != KeyguardSecurityModel.SecurityMode.None;
}
/**
@@ -1690,7 +1695,7 @@
*/
public boolean needsFullscreenBouncer() {
KeyguardSecurityModel.SecurityMode mode = mKeyguardSecurityModel.getSecurityMode(
- KeyguardUpdateMonitor.getCurrentUser());
+ mSelectedUserInteractor.getSelectedUserId());
return mode == KeyguardSecurityModel.SecurityMode.SimPin
|| mode == KeyguardSecurityModel.SecurityMode.SimPuk;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 1c88289..c624518 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -37,11 +37,12 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.logging.KeyguardUpdateMonitorLogger;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.res.R;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import dagger.Lazy;
@@ -64,6 +65,7 @@
private final Context mContext;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final LockPatternUtils mLockPatternUtils;
+ private final SelectedUserInteractor mUserInteractor;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
new UpdateMonitorCallback();
private final Lazy<KeyguardUnlockAnimationController> mUnlockAnimationControllerLazy;
@@ -120,11 +122,13 @@
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationController,
KeyguardUpdateMonitorLogger logger,
DumpManager dumpManager,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ SelectedUserInteractor userInteractor) {
mContext = context;
mLogger = logger;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
+ mUserInteractor = userInteractor;
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
mUnlockAnimationControllerLazy = keyguardUnlockAnimationController;
mFeatureFlags = featureFlags;
@@ -250,7 +254,7 @@
@VisibleForTesting
void update(boolean updateAlways) {
Trace.beginSection("KeyguardStateController#update");
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int user = mUserInteractor.getSelectedUserId();
boolean secure = mLockPatternUtils.isSecure(user);
boolean canDismissLockScreen = !secure || mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)
|| (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB && mDebugUnlocked);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt
index f88339a..7829d6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt
@@ -27,7 +27,7 @@
import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower
import com.android.systemui.user.data.source.UserRecord
import com.android.systemui.user.domain.interactor.GuestUserInteractor
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
import dagger.Lazy
import java.io.PrintWriter
@@ -41,7 +41,7 @@
@Inject
constructor(
@Application private val applicationContext: Context,
- private val userInteractorLazy: Lazy<UserInteractor>,
+ private val userSwitcherInteractorLazy: Lazy<UserSwitcherInteractor>,
private val guestUserInteractorLazy: Lazy<GuestUserInteractor>,
private val keyguardInteractorLazy: Lazy<KeyguardInteractor>,
private val activityStarter: ActivityStarter,
@@ -53,26 +53,29 @@
fun onUserSwitched()
}
- private val userInteractor: UserInteractor by lazy { userInteractorLazy.get() }
+ private val mUserSwitcherInteractor: UserSwitcherInteractor by lazy {
+ userSwitcherInteractorLazy.get()
+ }
private val guestUserInteractor: GuestUserInteractor by lazy { guestUserInteractorLazy.get() }
private val keyguardInteractor: KeyguardInteractor by lazy { keyguardInteractorLazy.get() }
- private val callbackCompatMap = mutableMapOf<UserSwitchCallback, UserInteractor.UserCallback>()
+ private val callbackCompatMap =
+ mutableMapOf<UserSwitchCallback, UserSwitcherInteractor.UserCallback>()
/** The current list of [UserRecord]. */
val users: ArrayList<UserRecord>
- get() = userInteractor.userRecords.value
+ get() = mUserSwitcherInteractor.userRecords.value
/** Whether the user switcher experience should use the simple experience. */
val isSimpleUserSwitcher: Boolean
- get() = userInteractor.isSimpleUserSwitcher
+ get() = mUserSwitcherInteractor.isSimpleUserSwitcher
val isUserSwitcherEnabled: Boolean
- get() = userInteractor.isUserSwitcherEnabled
+ get() = mUserSwitcherInteractor.isUserSwitcherEnabled
/** The [UserRecord] of the current user or `null` when none. */
val currentUserRecord: UserRecord?
- get() = userInteractor.selectedUserRecord.value
+ get() = mUserSwitcherInteractor.selectedUserRecord.value
/** The name of the current user of the device or `null`, when none is selected. */
val currentUserName: String?
@@ -81,8 +84,8 @@
LegacyUserUiHelper.getUserRecordName(
context = applicationContext,
record = it,
- isGuestUserAutoCreated = userInteractor.isGuestUserAutoCreated,
- isGuestUserResetting = userInteractor.isGuestUserResetting,
+ isGuestUserAutoCreated = mUserSwitcherInteractor.isGuestUserAutoCreated,
+ isGuestUserResetting = mUserSwitcherInteractor.isGuestUserResetting,
)
}
@@ -98,21 +101,21 @@
* @param dialogShower An optional [DialogShower] in case we need to show dialogs.
*/
fun onUserSelected(userId: Int, dialogShower: DialogShower?) {
- userInteractor.selectUser(userId, dialogShower)
+ mUserSwitcherInteractor.selectUser(userId, dialogShower)
}
/** Whether the guest user is configured to always be present on the device. */
val isGuestUserAutoCreated: Boolean
- get() = userInteractor.isGuestUserAutoCreated
+ get() = mUserSwitcherInteractor.isGuestUserAutoCreated
/** Whether the guest user is currently being reset. */
val isGuestUserResetting: Boolean
- get() = userInteractor.isGuestUserResetting
+ get() = mUserSwitcherInteractor.isGuestUserResetting
/** Registers an adapter to notify when the users change. */
fun addAdapter(adapter: WeakReference<BaseUserSwitcherAdapter>) {
- userInteractor.addCallback(
- object : UserInteractor.UserCallback {
+ mUserSwitcherInteractor.addCallback(
+ object : UserSwitcherInteractor.UserCallback {
override fun isEvictable(): Boolean {
return adapter.get() == null
}
@@ -129,7 +132,7 @@
record: UserRecord,
dialogShower: DialogShower?,
) {
- userInteractor.onRecordSelected(record, dialogShower)
+ mUserSwitcherInteractor.onRecordSelected(record, dialogShower)
}
/**
@@ -152,7 +155,7 @@
* `UserHandle.USER_NULL`, then switch immediately to the newly created guest user.
*/
fun removeGuestUser(guestUserId: Int, targetUserId: Int) {
- userInteractor.removeGuestUser(
+ mUserSwitcherInteractor.removeGuestUser(
guestUserId = guestUserId,
targetUserId = targetUserId,
)
@@ -168,7 +171,7 @@
* only if its ephemeral, else keep guest
*/
fun exitGuestUser(guestUserId: Int, targetUserId: Int, forceRemoveGuestOnExit: Boolean) {
- userInteractor.exitGuestUser(guestUserId, targetUserId, forceRemoveGuestOnExit)
+ mUserSwitcherInteractor.exitGuestUser(guestUserId, targetUserId, forceRemoveGuestOnExit)
}
/**
@@ -194,31 +197,31 @@
* The pictures are only loaded if they have not been loaded yet.
*/
fun refreshUsers() {
- userInteractor.refreshUsers()
+ mUserSwitcherInteractor.refreshUsers()
}
/** Adds a subscriber to when user switches. */
fun addUserSwitchCallback(callback: UserSwitchCallback) {
val interactorCallback =
- object : UserInteractor.UserCallback {
+ object : UserSwitcherInteractor.UserCallback {
override fun onUserStateChanged() {
callback.onUserSwitched()
}
}
callbackCompatMap[callback] = interactorCallback
- userInteractor.addCallback(interactorCallback)
+ mUserSwitcherInteractor.addCallback(interactorCallback)
}
/** Removes a previously-added subscriber. */
fun removeUserSwitchCallback(callback: UserSwitchCallback) {
val interactorCallback = callbackCompatMap.remove(callback)
if (interactorCallback != null) {
- userInteractor.removeCallback(interactorCallback)
+ mUserSwitcherInteractor.removeCallback(interactorCallback)
}
}
fun dump(pw: PrintWriter, args: Array<out String>) {
- userInteractor.dump(pw)
+ mUserSwitcherInteractor.dump(pw)
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt
index dc7fadd..12387893 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt
@@ -21,7 +21,6 @@
import android.os.Handler
import android.os.UserManager
import android.provider.Settings.Global.USER_SWITCHER_ENABLED
-import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
@@ -30,7 +29,6 @@
import com.android.systemui.qs.SettingObserver
import com.android.systemui.qs.footer.data.model.UserSwitcherStatusModel
import com.android.systemui.res.R
-import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.statusbar.policy.UserSwitcherController
import com.android.systemui.util.settings.GlobalSettings
@@ -61,10 +59,10 @@
@Background private val bgHandler: Handler,
@Background private val bgDispatcher: CoroutineDispatcher,
private val userManager: UserManager,
- private val userTracker: UserTracker,
private val userSwitcherController: UserSwitcherController,
private val userInfoController: UserInfoController,
private val globalSetting: GlobalSettings,
+ private val userRepository: UserRepository,
) : UserSwitcherRepository {
private val showUserSwitcherForSingleUser =
context.resources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)
@@ -80,7 +78,7 @@
globalSetting,
bgHandler,
USER_SWITCHER_ENABLED,
- userTracker.userId,
+ userRepository.getSelectedUserInfo().id,
) {
override fun handleValueChanged(value: Int, observedChange: Boolean) {
if (observedChange) {
@@ -147,7 +145,7 @@
private suspend fun isGuestUser(): Boolean {
return withContext(bgDispatcher) {
- userManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser())
+ userManager.isGuestUser(userRepository.getSelectedUserInfo().id)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
new file mode 100644
index 0000000..0e693d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
@@ -0,0 +1,38 @@
+package com.android.systemui.user.domain.interactor
+
+import android.annotation.UserIdInt
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags.REFACTOR_GETCURRENTUSER
+import com.android.systemui.user.data.repository.UserRepository
+import javax.inject.Inject
+
+/** Encapsulates business logic to interact the selected user */
+@SysUISingleton
+class SelectedUserInteractor
+@Inject
+constructor(
+ private val repository: UserRepository,
+ private val flags: FeatureFlagsClassic,
+) {
+
+ /**
+ * Returns the ID of the currently-selected user.
+ *
+ * @param bypassFlag this will ignore the feature flag and get the data from the repository
+ * instead. This is used for refactored methods that were previously pointing to `userTracker`
+ * and therefore should not be routed back to KeyguardUpdateMonitor when flag is disabled.
+ * KeyguardUpdateMonitor.getCurrentUser() is deprecated and will be removed soon (together
+ * with this flag).
+ */
+ @UserIdInt
+ @JvmOverloads
+ fun getSelectedUserId(bypassFlag: Boolean = false): Int {
+ if (bypassFlag || flags.isEnabled(REFACTOR_GETCURRENTUSER)) {
+ return repository.getSelectedUserInfo().id
+ } else {
+ return KeyguardUpdateMonitor.getCurrentUser()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
index dbc3bf3..e0d205f 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -83,9 +83,9 @@
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
-/** Encapsulates business logic to interact with user data and systems. */
+/** Encapsulates business logic to for the user switcher. */
@SysUISingleton
-class UserInteractor
+class UserSwitcherInteractor
@Inject
constructor(
@Application private val applicationContext: Context,
@@ -383,10 +383,6 @@
pw.println("isGuestUserAutoCreated=$isGuestUserAutoCreated")
}
- fun onDeviceBootCompleted() {
- guestUserInteractor.onDeviceBootCompleted()
- }
-
/** Switches to the user or executes the action represented by the given record. */
fun onRecordSelected(
record: UserRecord,
@@ -535,12 +531,6 @@
}
}
- /** Returns the ID of the currently-selected user. */
- @UserIdInt
- fun getSelectedUserId(): Int {
- return repository.getSelectedUserInfo().id
- }
-
private fun showDialog(request: ShowDialogRequestModel) {
_dialogShowRequests.value = request
}
@@ -664,7 +654,6 @@
// Connect to the new secondary user's service (purely to ensure that a persistent
// SystemUI application is created for that user)
-
if (userId != Process.myUserHandle().identifier) {
applicationContext.startServiceAsUser(
intent,
@@ -826,6 +815,6 @@
}
companion object {
- private const val TAG = "UserInteractor"
+ private const val TAG = "UserSwitcherInteractor"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
index 0930cb8..922dc05 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
@@ -33,7 +33,7 @@
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.tiles.UserDetailView
import com.android.systemui.user.UserSwitchFullscreenDialog
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import com.android.systemui.user.domain.model.ShowDialogRequestModel
import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
import dagger.Lazy
@@ -53,7 +53,7 @@
private val falsingManager: Lazy<FalsingManager>,
private val broadcastSender: Lazy<BroadcastSender>,
private val dialogLaunchAnimator: Lazy<DialogLaunchAnimator>,
- private val interactor: Lazy<UserInteractor>,
+ private val interactor: Lazy<UserSwitcherInteractor>,
private val userDetailAdapterProvider: Provider<UserDetailView.Adapter>,
private val eventLogger: Lazy<UiEventLogger>,
private val activityStarter: Lazy<ActivityStarter>,
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt
index 78edad7..2c425b19 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt
@@ -17,12 +17,10 @@
package com.android.systemui.user.ui.viewmodel
-import android.content.Context
import android.graphics.drawable.Drawable
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Text
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -33,8 +31,7 @@
class StatusBarUserChipViewModel
@Inject
constructor(
- @Application private val context: Context,
- interactor: UserInteractor,
+ interactor: UserSwitcherInteractor,
) {
/** Whether the status bar chip ui should be available */
val chipEnabled: Boolean = interactor.isStatusBarUserChipEnabled
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
index 20f0fa8c..4089889 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
@@ -20,9 +20,8 @@
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.ui.drawable.CircularDrawable
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.res.R
import com.android.systemui.user.domain.interactor.GuestUserInteractor
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
import com.android.systemui.user.shared.model.UserActionModel
import com.android.systemui.user.shared.model.UserModel
@@ -38,17 +37,17 @@
class UserSwitcherViewModel
@Inject
constructor(
- private val userInteractor: UserInteractor,
+ private val userSwitcherInteractor: UserSwitcherInteractor,
private val guestUserInteractor: GuestUserInteractor,
) {
/** The currently selected user. */
val selectedUser: Flow<UserViewModel> =
- userInteractor.selectedUser.map { user -> toViewModel(user) }
+ userSwitcherInteractor.selectedUser.map { user -> toViewModel(user) }
/** On-device users. */
val users: Flow<List<UserViewModel>> =
- userInteractor.users.map { models -> models.map { user -> toViewModel(user) } }
+ userSwitcherInteractor.users.map { models -> models.map { user -> toViewModel(user) } }
/** The maximum number of columns that the user selection grid should use. */
val maximumUserColumns: Flow<Int> = users.map { getMaxUserSwitcherItemColumns(it.size) }
@@ -61,7 +60,9 @@
val isMenuVisible: Flow<Boolean> = _isMenuVisible
/** The user action menu. */
val menu: Flow<List<UserActionViewModel>> =
- userInteractor.actions.map { actions -> actions.map { action -> toViewModel(action) } }
+ userSwitcherInteractor.actions.map { actions ->
+ actions.map { action -> toViewModel(action) }
+ }
/** Whether the button to open the user action menu is visible. */
val isOpenMenuButtonVisible: Flow<Boolean> = menu.map { it.isNotEmpty() }
@@ -175,7 +176,7 @@
isTablet = true,
),
onClicked = {
- userInteractor.executeAction(action = model)
+ userSwitcherInteractor.executeAction(action = model)
// We don't finish because we want to show a dialog over the full-screen UI and
// that dialog can be dismissed in case the user changes their mind and decides not
// to add a user.
@@ -195,7 +196,7 @@
null
} else {
{
- userInteractor.selectUser(model.id)
+ userSwitcherInteractor.selectUser(model.id)
userSwitched.value = true
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
index 83ff789..b3834f5 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
@@ -269,3 +269,108 @@
crossinline getValue: () -> T,
): StateFlow<T> =
changedSignals.map { getValue() }.stateIn(this, SharingStarted.Eagerly, getValue())
+
+inline fun <T1, T2, T3, T4, T5, T6, R> combine(
+ flow: Flow<T1>,
+ flow2: Flow<T2>,
+ flow3: Flow<T3>,
+ flow4: Flow<T4>,
+ flow5: Flow<T5>,
+ flow6: Flow<T6>,
+ crossinline transform: suspend (T1, T2, T3, T4, T5, T6) -> R
+): Flow<R> {
+ return kotlinx.coroutines.flow.combine(flow, flow2, flow3, flow4, flow5, flow6) {
+ args: Array<*> ->
+ @Suppress("UNCHECKED_CAST")
+ transform(
+ args[0] as T1,
+ args[1] as T2,
+ args[2] as T3,
+ args[3] as T4,
+ args[4] as T5,
+ args[5] as T6
+ )
+ }
+}
+
+inline fun <T1, T2, T3, T4, T5, T6, T7, R> combine(
+ flow: Flow<T1>,
+ flow2: Flow<T2>,
+ flow3: Flow<T3>,
+ flow4: Flow<T4>,
+ flow5: Flow<T5>,
+ flow6: Flow<T6>,
+ flow7: Flow<T7>,
+ crossinline transform: suspend (T1, T2, T3, T4, T5, T6, T7) -> R
+): Flow<R> {
+ return kotlinx.coroutines.flow.combine(flow, flow2, flow3, flow4, flow5, flow6, flow7) {
+ args: Array<*> ->
+ @Suppress("UNCHECKED_CAST")
+ transform(
+ args[0] as T1,
+ args[1] as T2,
+ args[2] as T3,
+ args[3] as T4,
+ args[4] as T5,
+ args[5] as T6,
+ args[6] as T7
+ )
+ }
+}
+
+inline fun <T1, T2, T3, T4, T5, T6, T7, T8, R> combine(
+ flow: Flow<T1>,
+ flow2: Flow<T2>,
+ flow3: Flow<T3>,
+ flow4: Flow<T4>,
+ flow5: Flow<T5>,
+ flow6: Flow<T6>,
+ flow7: Flow<T7>,
+ flow8: Flow<T8>,
+ crossinline transform: suspend (T1, T2, T3, T4, T5, T6, T7, T8) -> R
+): Flow<R> {
+ return kotlinx.coroutines.flow.combine(flow, flow2, flow3, flow4, flow5, flow6, flow7, flow8) {
+ args: Array<*> ->
+ @Suppress("UNCHECKED_CAST")
+ transform(
+ args[0] as T1,
+ args[1] as T2,
+ args[2] as T3,
+ args[3] as T4,
+ args[4] as T5,
+ args[5] as T6,
+ args[6] as T7,
+ args[7] as T8
+ )
+ }
+}
+
+inline fun <T1, T2, T3, T4, T5, T6, T7, T8, T9, R> combine(
+ flow: Flow<T1>,
+ flow2: Flow<T2>,
+ flow3: Flow<T3>,
+ flow4: Flow<T4>,
+ flow5: Flow<T5>,
+ flow6: Flow<T6>,
+ flow7: Flow<T7>,
+ flow8: Flow<T8>,
+ flow9: Flow<T9>,
+ crossinline transform: suspend (T1, T2, T3, T4, T5, T6, T7, T8, T9) -> R
+): Flow<R> {
+ return kotlinx.coroutines.flow.combine(
+ flow, flow2, flow3, flow4, flow5, flow6, flow7, flow8, flow9
+ ) { args: Array<*> ->
+ @Suppress("UNCHECKED_CAST")
+ transform(
+ args[0] as T1,
+ args[1] as T2,
+ args[2] as T3,
+ args[3] as T4,
+ args[4] as T5,
+ args[5] as T6,
+ args[6] as T7,
+ args[6] as T8,
+ args[6] as T9,
+ )
+ }
+}
\ No newline at end of file
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 9b06a37..d566725 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
@@ -191,7 +191,7 @@
}
@Override
- protected boolean initDataInjectionImpl(boolean enable) {
+ protected boolean initDataInjectionImpl(boolean enable, @DataInjectionMode int mode) {
throw new UnsupportedOperationException("not implemented");
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java
index 2d1e622..50d1547 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Events.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java
@@ -234,6 +234,10 @@
VOLUME_DIALOG_SLIDER(150),
@UiEvent(doc = "The audio stream was set to silent via slider")
VOLUME_DIALOG_SLIDER_TO_ZERO(151),
+ @UiEvent(doc = "ODI captions was clicked")
+ VOLUME_DIALOG_ODI_CAPTIONS_CLICKED(1503),
+ @UiEvent(doc = "ODI captions tooltip dismiss was clicked")
+ VOLUME_DIALOG_ODI_CAPTIONS_TOOLTIP_CLICKED(1504),
@UiEvent(doc = "The audio volume was adjusted to silent via key")
VOLUME_KEY_TO_ZERO(152),
@UiEvent(doc = "The audio volume was adjusted to non-silent via key")
@@ -362,6 +366,10 @@
if (tag == EVENT_SETTINGS_CLICK) {
sLegacyLogger.action(MetricsEvent.ACTION_VOLUME_SETTINGS);
sUiEventLogger.log(VolumeDialogEvent.VOLUME_DIALOG_SETTINGS_CLICK);
+ } else if (tag == EVENT_ODI_CAPTIONS_CLICK) {
+ sUiEventLogger.log(VolumeDialogEvent.VOLUME_DIALOG_ODI_CAPTIONS_CLICKED);
+ } else if (tag == EVENT_ODI_CAPTIONS_TOOLTIP_CLICK) {
+ sUiEventLogger.log(VolumeDialogEvent.VOLUME_DIALOG_ODI_CAPTIONS_TOOLTIP_CLICKED);
}
return sb.toString();
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 929b91c..0ff308e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -120,7 +120,6 @@
import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.Prefs;
-import com.android.systemui.res.R;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
@@ -129,6 +128,7 @@
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.plugins.VolumeDialogController.State;
import com.android.systemui.plugins.VolumeDialogController.StreamState;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DevicePostureController;
@@ -287,7 +287,7 @@
private boolean mIsAnimatingDismiss = false;
private boolean mHasSeenODICaptionsTooltip;
private ViewStub mODICaptionsTooltipViewStub;
- private View mODICaptionsTooltipView = null;
+ @VisibleForTesting View mODICaptionsTooltipView = null;
private final boolean mUseBackgroundBlur;
private Consumer<Boolean> mCrossWindowBlurEnabledListener;
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index 9dca013..aea3030 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -109,6 +109,7 @@
private WallpaperManager mWallpaperManager;
private final WallpaperLocalColorExtractor mWallpaperLocalColorExtractor;
private SurfaceHolder mSurfaceHolder;
+ private boolean mDrawn = false;
@VisibleForTesting
static final int MIN_SURFACE_WIDTH = 128;
@VisibleForTesting
@@ -239,6 +240,7 @@
private void drawFrameSynchronized() {
synchronized (mLock) {
+ if (mDrawn) return;
drawFrameInternal();
}
}
@@ -276,6 +278,7 @@
Rect dest = mSurfaceHolder.getSurfaceFrame();
try {
canvas.drawBitmap(bitmap, null, dest, null);
+ mDrawn = true;
} finally {
surface.unlockCanvasAndPost(canvas);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 897c4da..1e801ae 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -33,6 +33,7 @@
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
+import android.util.Log;
import android.view.Display;
import android.view.KeyEvent;
@@ -66,6 +67,7 @@
import com.android.wm.shell.sysui.ShellInterface;
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -371,6 +373,13 @@
@Override
public void dump(PrintWriter pw, String[] args) {
+ Log.d(TAG, "Dumping with args: " + String.join(", ", args));
+
+ // Strip out the SysUI "dependency" arg before sending to WMShell
+ if (args[0].equals("dependency")) {
+ args = Arrays.copyOfRange(args, 1, args.length);
+ }
+
// Handle commands if provided
if (mShell.handleCommand(args, pw)) {
return;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index 81fef7a..b31f630a4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -35,9 +35,12 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
+import java.io.PrintWriter
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -45,26 +48,21 @@
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import java.io.PrintWriter
@SmallTest
class ActiveUnlockConfigTest : SysuiTestCase() {
private lateinit var secureSettings: FakeSettings
- @Mock
- private lateinit var contentResolver: ContentResolver
- @Mock
- private lateinit var handler: Handler
- @Mock
- private lateinit var dumpManager: DumpManager
- @Mock
- private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var contentResolver: ContentResolver
+ @Mock private lateinit var handler: Handler
+ @Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var mockPrintWriter: PrintWriter
- @Captor
- private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver>
+ @Captor private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver>
private lateinit var activeUnlockConfig: ActiveUnlockConfig
private var currentUser: Int = 0
@@ -73,14 +71,16 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- currentUser = KeyguardUpdateMonitor.getCurrentUser()
+ whenever(selectedUserInteractor.getSelectedUserId()).thenReturn(currentUser)
secureSettings = FakeSettings()
- activeUnlockConfig = ActiveUnlockConfig(
+ activeUnlockConfig =
+ ActiveUnlockConfig(
handler,
secureSettings,
contentResolver,
+ selectedUserInteractor,
dumpManager
- )
+ )
}
@Test
@@ -92,8 +92,9 @@
fun onWakeupSettingChanged() {
// GIVEN no active unlock settings enabled
assertFalse(
- activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE)
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE
+ )
)
// WHEN unlock on wake is allowed
@@ -102,16 +103,19 @@
// THEN active unlock triggers allowed on: wake, unlock-intent, and biometric failure
assertTrue(
- activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE)
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE
+ )
)
assertTrue(
- activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT)
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+ )
)
assertTrue(
- activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL)
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL
+ )
)
}
@@ -119,8 +123,9 @@
fun onUnlockIntentSettingChanged() {
// GIVEN no active unlock settings enabled
assertFalse(
- activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT)
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+ )
)
// WHEN unlock on biometric failed is allowed
@@ -128,12 +133,21 @@
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT))
// THEN active unlock triggers allowed on: biometric failure ONLY
- assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE))
- assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT))
- assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL))
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE
+ )
+ )
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+ )
+ )
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL
+ )
+ )
}
@Test
@@ -141,24 +155,39 @@
// GIVEN no active unlock settings enabled and triggering unlock intent on biometric
// enrollment setting is disabled (empty string is disabled, null would use the default)
secureSettings.putStringForUser(
- ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, "", currentUser)
- updateSetting(secureSettings.getUriFor(
- ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
- ))
- assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL))
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+ "",
+ currentUser
+ )
+ updateSetting(
+ secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
+ )
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL
+ )
+ )
// WHEN unlock on biometric failed is allowed
secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
// THEN active unlock triggers allowed on: biometric failure ONLY
- assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE))
- assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT))
- assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL))
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE
+ )
+ )
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+ )
+ )
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL
+ )
+ )
}
@Test
@@ -168,16 +197,21 @@
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
// WHEN face error timeout (3), allow trigger active unlock
- secureSettings.putStringForUser(
- ACTIVE_UNLOCK_ON_FACE_ERRORS, "3", currentUser)
+ secureSettings.putStringForUser(ACTIVE_UNLOCK_ON_FACE_ERRORS, "3", currentUser)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS))
// THEN active unlock triggers allowed on error TIMEOUT
- assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceError(
- BiometricFaceConstants.FACE_ERROR_TIMEOUT))
+ assertTrue(
+ activeUnlockConfig.shouldRequestActiveUnlockOnFaceError(
+ BiometricFaceConstants.FACE_ERROR_TIMEOUT
+ )
+ )
- assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceError(
- BiometricFaceConstants.FACE_ERROR_CANCELED))
+ assertFalse(
+ activeUnlockConfig.shouldRequestActiveUnlockOnFaceError(
+ BiometricFaceConstants.FACE_ERROR_CANCELED
+ )
+ )
}
@Test
@@ -189,21 +223,34 @@
// WHEN face acquiredMsg DARK_GLASSESand MOUTH_COVERING are allowed to trigger
secureSettings.putStringForUser(
ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
- "${BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED}" +
- "|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}",
- currentUser)
+ "${BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED}" +
+ "|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}",
+ currentUser
+ )
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
// THEN active unlock triggers allowed on acquired messages DARK_GLASSES & MOUTH_COVERING
- assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
- BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED))
- assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
- BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED))
+ assertTrue(
+ activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
+ BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED
+ )
+ )
+ assertTrue(
+ activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
+ BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED
+ )
+ )
- assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
- BiometricFaceConstants.FACE_ACQUIRED_GOOD))
- assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
- BiometricFaceConstants.FACE_ACQUIRED_NOT_DETECTED))
+ assertFalse(
+ activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
+ BiometricFaceConstants.FACE_ACQUIRED_GOOD
+ )
+ )
+ assertFalse(
+ activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
+ BiometricFaceConstants.FACE_ACQUIRED_NOT_DETECTED
+ )
+ )
}
@Test
@@ -221,14 +268,19 @@
secureSettings.putStringForUser(
ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
- "${ActiveUnlockConfig.BiometricType.NONE.intValue}", currentUser)
- updateSetting(secureSettings.getUriFor(
- ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
- ))
+ "${ActiveUnlockConfig.BiometricType.NONE.intValue}",
+ currentUser
+ )
+ updateSetting(
+ secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
+ )
// THEN active unlock triggers allowed on unlock intent
- assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT))
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+ )
+ )
}
@Test
@@ -245,33 +297,43 @@
// WHEN unlock intent is allowed when ONLY fingerprint is enrolled or NO biometircs
// are enrolled
secureSettings.putStringForUser(
- ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
- "${ActiveUnlockConfig.BiometricType.ANY_FACE.intValue}" +
- "|${ActiveUnlockConfig.BiometricType.ANY_FINGERPRINT.intValue}",
- currentUser)
- updateSetting(secureSettings.getUriFor(
- ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
- ))
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+ "${ActiveUnlockConfig.BiometricType.ANY_FACE.intValue}" +
+ "|${ActiveUnlockConfig.BiometricType.ANY_FINGERPRINT.intValue}",
+ currentUser
+ )
+ updateSetting(
+ secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
+ )
// THEN active unlock triggers NOT allowed on unlock intent
- assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT))
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+ )
+ )
// WHEN fingerprint ONLY enrolled
`when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(false)
`when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(true)
// THEN active unlock triggers allowed on unlock intent
- assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT))
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+ )
+ )
// WHEN face ONLY enrolled
`when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(true)
`when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false)
// THEN active unlock triggers allowed on unlock intent
- assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT))
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+ )
+ )
}
@Test
@@ -280,7 +342,8 @@
secureSettings.putIntForUser(
ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
PowerManager.WAKE_REASON_LIFT,
- currentUser)
+ currentUser
+ )
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
// THEN only WAKE_REASON_LIFT is considered an unlock intent
@@ -299,16 +362,18 @@
secureSettings.putStringForUser(
ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
PowerManager.WAKE_REASON_LIFT.toString() +
- "|" +
- PowerManager.WAKE_REASON_TAP.toString(),
+ "|" +
+ PowerManager.WAKE_REASON_TAP.toString(),
currentUser
)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
// THEN WAKE_REASON_LIFT and WAKE_REASON TAP are considered an unlock intent
for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
- if (wakeReason == PowerManager.WAKE_REASON_LIFT ||
- wakeReason == PowerManager.WAKE_REASON_TAP) {
+ if (
+ wakeReason == PowerManager.WAKE_REASON_LIFT ||
+ wakeReason == PowerManager.WAKE_REASON_TAP
+ ) {
assertTrue(activeUnlockConfig.isWakeupConsideredUnlockIntent(wakeReason))
} else {
assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent(wakeReason))
@@ -316,26 +381,36 @@
}
assertTrue(activeUnlockConfig.isWakeupConsideredUnlockIntent(PowerManager.WAKE_REASON_LIFT))
assertTrue(activeUnlockConfig.isWakeupConsideredUnlockIntent(PowerManager.WAKE_REASON_TAP))
- assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent(
- PowerManager.WAKE_REASON_UNFOLD_DEVICE))
+ assertFalse(
+ activeUnlockConfig.isWakeupConsideredUnlockIntent(
+ PowerManager.WAKE_REASON_UNFOLD_DEVICE
+ )
+ )
}
@Test
fun isWakeupConsideredUnlockIntent_emptyValues() {
// GIVEN lift and tap are considered an unlock intent
- secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS, " ",
- currentUser)
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
+ " ",
+ currentUser
+ )
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
// THEN no wake up gestures are considered an unlock intent
for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent(wakeReason))
}
- assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent(
- PowerManager.WAKE_REASON_LIFT))
+ assertFalse(
+ activeUnlockConfig.isWakeupConsideredUnlockIntent(PowerManager.WAKE_REASON_LIFT)
+ )
assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent(PowerManager.WAKE_REASON_TAP))
- assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent(
- PowerManager.WAKE_REASON_UNFOLD_DEVICE))
+ assertFalse(
+ activeUnlockConfig.isWakeupConsideredUnlockIntent(
+ PowerManager.WAKE_REASON_UNFOLD_DEVICE
+ )
+ )
}
@Test
@@ -343,11 +418,12 @@
verifyRegisterSettingObserver()
// GIVEN lift is considered an unlock intent
- secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
- PowerManager.WAKE_REASON_LIFT.toString(), currentUser)
- updateSetting(secureSettings.getUriFor(
- ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD
- ))
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
+ PowerManager.WAKE_REASON_LIFT.toString(),
+ currentUser
+ )
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD))
// THEN only WAKE_REASON_LIFT is considered an unlock intent
for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
@@ -364,11 +440,12 @@
verifyRegisterSettingObserver()
// GIVEN lift and tap are considered an unlock intent
- secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
- " ", currentUser)
- updateSetting(secureSettings.getUriFor(
- ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD
- ))
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
+ " ",
+ currentUser
+ )
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD))
// THEN no wake up gestures are considered an unlock intent
for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
@@ -381,20 +458,21 @@
verifyRegisterSettingObserver()
// GIVEN lift and tap are considered an unlock intent
- secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
PowerManager.WAKE_REASON_LIFT.toString() +
- "|" +
- PowerManager.WAKE_REASON_TAP.toString(),
+ "|" +
+ PowerManager.WAKE_REASON_TAP.toString(),
currentUser
)
- updateSetting(secureSettings.getUriFor(
- ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD
- ))
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD))
// THEN WAKE_REASON_LIFT and WAKE_REASON TAP are considered an unlock intent
for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
- if (wakeReason == PowerManager.WAKE_REASON_LIFT ||
- wakeReason == PowerManager.WAKE_REASON_TAP) {
+ if (
+ wakeReason == PowerManager.WAKE_REASON_LIFT ||
+ wakeReason == PowerManager.WAKE_REASON_TAP
+ ) {
assertTrue(activeUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason))
} else {
assertFalse(activeUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason))
@@ -405,13 +483,16 @@
@Test
fun dump_onUnlockIntentWhenBiometricEnrolled_invalidNum_noArrayOutOfBoundsException() {
// GIVEN an invalid input (-1)
- secureSettings.putStringForUser(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
- "-1", currentUser)
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+ "-1",
+ currentUser
+ )
// WHEN the setting updates
- updateSetting(secureSettings.getUriFor(
- ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
- ))
+ updateSetting(
+ secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
+ )
// THEN no exception thrown
activeUnlockConfig.dump(mockPrintWriter, emptyArray())
@@ -419,12 +500,7 @@
private fun updateSetting(uri: Uri) {
verifyRegisterSettingObserver()
- settingsObserverCaptor.value.onChange(
- false,
- listOf(uri),
- 0,
- 0 /* flags */
- )
+ settingsObserverCaptor.value.onChange(false, listOf(uri), 0, 0 /* flags */)
}
private fun verifyRegisterSettingObserver() {
@@ -433,19 +509,21 @@
verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS))
verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
- verifyRegisterSettingObserver(secureSettings.getUriFor(
- ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
- ))
- verifyRegisterSettingObserver(secureSettings.getUriFor(
- ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
- ))
+ verifyRegisterSettingObserver(
+ secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
+ )
+ verifyRegisterSettingObserver(
+ secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS)
+ )
}
private fun verifyRegisterSettingObserver(uri: Uri) {
- verify(contentResolver).registerContentObserver(
+ verify(contentResolver)
+ .registerContentObserver(
eq(uri),
eq(false),
capture(settingsObserverCaptor),
- eq(UserHandle.USER_ALL))
+ eq(UserHandle.USER_ALL)
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
index d506584..cdd0eb0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
@@ -50,6 +50,7 @@
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import org.junit.After;
import org.junit.Before;
@@ -64,7 +65,7 @@
@SmallTest
public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
- private static final int TARGET_USER_ID = KeyguardUpdateMonitor.getCurrentUser();
+ private static final int TARGET_USER_ID = 0;
private AdminSecondaryLockScreenController mTestController;
private ComponentName mComponentName;
@@ -80,12 +81,15 @@
private KeyguardSecurityCallback mKeyguardCallback;
@Mock
private KeyguardUpdateMonitor mUpdateMonitor;
+ @Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ when(mSelectedUserInteractor.getSelectedUserId()).thenReturn(TARGET_USER_ID);
mKeyguardSecurityContainer = spy(new KeyguardSecurityContainer(mContext));
mKeyguardSecurityContainer.setId(View.generateViewId());
@@ -106,7 +110,8 @@
new Binder())).getSurfacePackage();
mTestController = new AdminSecondaryLockScreenController.Factory(
- mContext, mKeyguardSecurityContainer, mUpdateMonitor, mHandler)
+ mContext, mKeyguardSecurityContainer, mUpdateMonitor, mHandler,
+ mSelectedUserInteractor)
.create(mKeyguardCallback);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
index 30fed0b..c61b11a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
@@ -29,6 +29,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.shade.ShadeController
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
@@ -59,6 +60,7 @@
@Mock lateinit var metricsLogger: MetricsLogger
@Mock lateinit var lockPatternUtils: LockPatternUtils
@Mock lateinit var packageManager: PackageManager
+ @Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor
val fakeSystemClock = FakeSystemClock()
val mainExecutor = FakeExecutor(fakeSystemClock)
val backgroundExecutor = FakeExecutor(fakeSystemClock)
@@ -79,7 +81,8 @@
metricsLogger,
lockPatternUtils,
mainExecutor,
- backgroundExecutor
+ backgroundExecutor,
+ mSelectedUserInteractor,
)
context.setMockPackageManager(packageManager)
Mockito.`when`(emergencyButton.context).thenReturn(context)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index 42f65f6..7f20d9a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -37,12 +37,13 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.res.R;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import org.junit.Before;
import org.junit.Test;
@@ -81,6 +82,8 @@
private EmergencyButtonController mEmergencyButtonController;
private FakeFeatureFlags mFeatureFlags;
+ @Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController;
@Before
@@ -105,7 +108,7 @@
return new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
mKeyguardMessageAreaControllerFactory, mLatencyTracker, mFalsingCollector,
- mEmergencyButtonController, mFeatureFlags) {
+ mEmergencyButtonController, mFeatureFlags, mSelectedUserInteractor) {
@Override
void resetState() {
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
index 91b544b..634dac1 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
@@ -25,6 +25,7 @@
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.SessionTracker
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -34,8 +35,8 @@
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class)
@SmallTest
@@ -50,6 +51,8 @@
lateinit var sessionTracker: SessionTracker
@Mock
lateinit var sessionId: InstanceId
+ @Mock
+ lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Captor
lateinit var updateMonitorCallbackCaptor: ArgumentCaptor<KeyguardUpdateMonitorCallback>
@@ -65,7 +68,8 @@
keyguardBiometricLockoutLogger = KeyguardBiometricLockoutLogger(
uiEventLogger,
keyguardUpdateMonitor,
- sessionTracker)
+ sessionTracker,
+ mSelectedUserInteractor)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index d8a2c5f..3fbcf6d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -24,12 +24,13 @@
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.DevicePostureController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.mockito.whenever
import org.junit.Before
@@ -62,6 +63,7 @@
@Mock lateinit var mainExecutor: DelayableExecutor
@Mock lateinit var falsingCollector: FalsingCollector
@Mock lateinit var keyguardViewController: KeyguardViewController
+ @Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea
@Mock
private lateinit var mKeyguardMessageAreaController:
@@ -106,7 +108,8 @@
falsingCollector,
keyguardViewController,
postureController,
- fakeFeatureFlags
+ fakeFeatureFlags,
+ mSelectedUserInteractor,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index dc1618d..74c9225 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -24,15 +24,16 @@
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED
import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -75,6 +76,9 @@
KeyguardMessageAreaController.Factory
@Mock
+ private lateinit var mSelectedUserInteractor: SelectedUserInteractor
+
+ @Mock
private lateinit var mKeyguardMessageAreaController:
KeyguardMessageAreaController<BouncerKeyguardMessageArea>
@@ -108,7 +112,8 @@
mEmergencyButtonController,
mKeyguardMessageAreaControllerFactory,
mPostureController,
- fakeFeatureFlags
+ fakeFeatureFlags,
+ mSelectedUserInteractor
)
mKeyguardPatternView.onAttachedToWindow()
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index 4a24e4a..d41c249 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -34,10 +34,10 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingCollectorFake;
-import com.android.systemui.classifier.SingleTapClassifier;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.res.R;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import org.junit.Before;
import org.junit.Test;
@@ -76,11 +76,11 @@
private EmergencyButtonController mEmergencyButtonController;
private FalsingCollector mFalsingCollector = new FalsingCollectorFake();
@Mock
- private SingleTapClassifier mSingleTapClassifier;
- @Mock
private View mDeleteButton;
@Mock
private View mOkButton;
+ @Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
private NumPadKey[] mButtons = new NumPadKey[]{};
private KeyguardPinBasedInputViewController mKeyguardPinViewController;
@@ -108,7 +108,8 @@
mKeyguardPinViewController = new KeyguardPinBasedInputViewController(mPinBasedInputView,
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener,
- mEmergencyButtonController, mFalsingCollector, featureFlags) {
+ mEmergencyButtonController, mFalsingCollector, featureFlags,
+ mSelectedUserInteractor) {
@Override
public void onResume(int reason) {
super.onResume(reason);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 9df4dd4..80d45bc 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -25,15 +25,16 @@
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED
import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -84,6 +85,7 @@
@Mock private val mEmergencyButtonController: EmergencyButtonController? = null
private val falsingCollector: FalsingCollector = FalsingCollectorFake()
@Mock lateinit var postureController: DevicePostureController
+ @Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock lateinit var featureFlags: FeatureFlags
@Mock lateinit var passwordTextView: PasswordTextView
@@ -133,7 +135,8 @@
mEmergencyButtonController,
falsingCollector,
postureController,
- featureFlags
+ featureFlags,
+ mSelectedUserInteractor,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 20d4eb9..fda4133 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -63,7 +63,7 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.policy.UserSwitcherController
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argThat
@@ -136,7 +136,7 @@
@Mock private lateinit var telephonyManager: TelephonyManager
@Mock private lateinit var viewMediatorCallback: ViewMediatorCallback
@Mock private lateinit var audioManager: AudioManager
- @Mock private lateinit var userInteractor: UserInteractor
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock private lateinit var faceAuthAccessibilityDelegate: FaceAuthAccessibilityDelegate
@Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
@Mock private lateinit var postureController: DevicePostureController
@@ -215,10 +215,10 @@
null,
keyguardViewController,
postureController,
- featureFlags
+ featureFlags,
+ mSelectedUserInteractor,
)
- whenever(userInteractor.getSelectedUserId()).thenReturn(TARGET_USER_ID)
sceneTestUtils = SceneTestUtils(this)
sceneInteractor = sceneTestUtils.sceneInteractor()
keyguardTransitionInteractor =
@@ -260,7 +260,7 @@
mock(),
mock(),
{ JavaAdapter(sceneTestUtils.testScope.backgroundScope) },
- userInteractor,
+ mSelectedUserInteractor,
deviceProvisionedController,
faceAuthAccessibilityDelegate,
keyguardTransitionInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index 4290b8b..94c3bde 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -24,11 +24,12 @@
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.res.R
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import org.junit.Before
import org.junit.Test
@@ -58,6 +59,7 @@
@Mock private lateinit var telephonyManager: TelephonyManager
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var emergencyButtonController: EmergencyButtonController
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock
private lateinit var keyguardMessageAreaController:
KeyguardMessageAreaController<BouncerKeyguardMessageArea>
@@ -90,6 +92,7 @@
falsingCollector,
emergencyButtonController,
fakeFeatureFlags,
+ mSelectedUserInteractor
)
underTest.init()
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index 31ee641..7b1f302 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -24,11 +24,12 @@
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.res.R
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import org.junit.Before
import org.junit.Test
@@ -54,6 +55,7 @@
@Mock private lateinit var telephonyManager: TelephonyManager
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var emergencyButtonController: EmergencyButtonController
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock
private lateinit var keyguardMessageAreaController:
KeyguardMessageAreaController<BouncerKeyguardMessageArea>
@@ -89,6 +91,7 @@
falsingCollector,
emergencyButtonController,
fakeFeatureFlags,
+ mSelectedUserInteractor,
)
underTest.init()
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 47be236..aabdcb7 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -38,7 +38,6 @@
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING_RESTARTING;
import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT;
-import static com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
@@ -82,7 +81,6 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricAuthenticator;
@@ -157,8 +155,8 @@
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.telephony.TelephonyListenerManager;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.settings.GlobalSettings;
-import com.android.systemui.util.settings.SecureSettings;
import org.junit.After;
import org.junit.Assert;
@@ -240,8 +238,6 @@
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@Mock
- private SecureSettings mSecureSettings;
- @Mock
private TelephonyManager mTelephonyManager;
@Mock
private SensorPrivacyManager mSensorPrivacyManager;
@@ -278,18 +274,17 @@
@Mock
private UsbPortStatus mUsbPortStatus;
@Mock
- private Uri mURI;
- @Mock
private TaskStackChangeListeners mTaskStackChangeListeners;
@Mock
private IActivityTaskManager mActivityTaskManager;
@Mock
private WakefulnessLifecycle mWakefulness;
+ @Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
private List<FaceSensorPropertiesInternal> mFaceSensorProperties;
private List<FingerprintSensorPropertiesInternal> mFingerprintSensorProperties;
private final int mCurrentUserId = 100;
- private final UserInfo mCurrentUserInfo = new UserInfo(mCurrentUserId, "Test user", 0);
@Captor
private ArgumentCaptor<IBiometricEnabledOnKeyguardCallback>
@@ -337,8 +332,8 @@
.startMocking();
ExtendedMockito.doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
.when(SubscriptionManager::getDefaultSubscriptionId);
- KeyguardUpdateMonitor.setCurrentUser(mCurrentUserId);
- when(mUserTracker.getUserId()).thenReturn(mCurrentUserId);
+ when(mSelectedUserInteractor.getSelectedUserId()).thenReturn(mCurrentUserId);
+ when(mSelectedUserInteractor.getSelectedUserId(anyBoolean())).thenReturn(mCurrentUserId);
mContext.getOrCreateTestableResources().addOverride(
com.android.systemui.res.R.integer.config_face_auth_supported_posture,
@@ -351,13 +346,11 @@
mContext.getOrCreateTestableResources().addOverride(com.android.systemui.res
.R.array.config_fingerprint_listen_on_occluding_activity_packages,
- new String[]{ PKG_ALLOWING_FP_LISTEN_ON_OCCLUDING_ACTIVITY });
+ new String[]{PKG_ALLOWING_FP_LISTEN_ON_OCCLUDING_ACTIVITY});
mTestableLooper = TestableLooper.get(this);
allowTestableLooperAsMainThread();
- when(mSecureSettings.getUriFor(anyString())).thenReturn(mURI);
-
final ContentResolver contentResolver = mContext.getContentResolver();
ExtendedMockito.spyOn(contentResolver);
doNothing().when(contentResolver)
@@ -1005,11 +998,14 @@
@Test
public void trustAgentHasTrust() {
// WHEN user has trust
- mKeyguardUpdateMonitor.onTrustChanged(true, true, getCurrentUser(), 0, null);
+ mKeyguardUpdateMonitor.onTrustChanged(true, true,
+ mSelectedUserInteractor.getSelectedUserId(), 0, null);
// THEN user is considered as "having trust" and bouncer can be skipped
- Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser()));
- Assert.assertTrue(mKeyguardUpdateMonitor.getUserCanSkipBouncer(getCurrentUser()));
+ Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(
+ mSelectedUserInteractor.getSelectedUserId()));
+ Assert.assertTrue(mKeyguardUpdateMonitor.getUserCanSkipBouncer(
+ mSelectedUserInteractor.getSelectedUserId()));
}
@Test
@@ -1027,15 +1023,19 @@
@Test
public void trustAgentHasTrust_fingerprintLockout() {
// GIVEN user has trust
- mKeyguardUpdateMonitor.onTrustChanged(true, true, getCurrentUser(), 0, null);
- Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser()));
+ mKeyguardUpdateMonitor.onTrustChanged(true, true,
+ mSelectedUserInteractor.getSelectedUserId(), 0, null);
+ Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(
+ mSelectedUserInteractor.getSelectedUserId()));
// WHEN fingerprint is lock out
fingerprintErrorTemporaryLockOut();
// THEN user is NOT considered as "having trust" and bouncer cannot be skipped
- Assert.assertFalse(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser()));
- Assert.assertFalse(mKeyguardUpdateMonitor.getUserCanSkipBouncer(getCurrentUser()));
+ Assert.assertFalse(mKeyguardUpdateMonitor.getUserHasTrust(
+ mSelectedUserInteractor.getSelectedUserId()));
+ Assert.assertFalse(mKeyguardUpdateMonitor.getUserCanSkipBouncer(
+ mSelectedUserInteractor.getSelectedUserId()));
}
@Test
@@ -1217,7 +1217,7 @@
mTestableLooper.processAllMessages();
lockscreenBypassIsAllowed();
mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */,
- KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */,
+ mSelectedUserInteractor.getSelectedUserId(), 0 /* flags */,
new ArrayList<>());
keyguardIsVisible();
verifyFaceAuthenticateCall();
@@ -1249,7 +1249,7 @@
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */,
- KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */, new ArrayList<>());
+ mSelectedUserInteractor.getSelectedUserId(), 0 /* flags */, new ArrayList<>());
keyguardIsVisible();
verifyFaceAuthenticateNeverCalled();
}
@@ -1287,7 +1287,7 @@
public void testOnFaceAuthenticated_skipsFaceWhenAuthenticated() {
// test whether face will be skipped if authenticated, so the value of isClass3Biometric
// doesn't matter here
- mKeyguardUpdateMonitor.onFaceAuthenticated(KeyguardUpdateMonitor.getCurrentUser(),
+ mKeyguardUpdateMonitor.onFaceAuthenticated(mSelectedUserInteractor.getSelectedUserId(),
true /* isClass3Biometric */);
setKeyguardBouncerVisibility(true);
mTestableLooper.processAllMessages();
@@ -1333,7 +1333,7 @@
@Test
public void testGetUserCanSkipBouncer_whenFace() {
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int user = mSelectedUserInteractor.getSelectedUserId();
mKeyguardUpdateMonitor.onFaceAuthenticated(user, true /* isClass3Biometric */);
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
}
@@ -1342,14 +1342,14 @@
public void testGetUserCanSkipBouncer_whenFace_nonStrongAndDisallowed() {
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(false /* isClass3Biometric */))
.thenReturn(false);
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int user = mSelectedUserInteractor.getSelectedUserId();
mKeyguardUpdateMonitor.onFaceAuthenticated(user, false /* isClass3Biometric */);
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isFalse();
}
@Test
public void testGetUserCanSkipBouncer_whenFingerprint() {
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int user = mSelectedUserInteractor.getSelectedUserId();
mKeyguardUpdateMonitor.onFingerprintAuthenticated(user, true /* isClass3Biometric */);
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
}
@@ -1358,7 +1358,7 @@
public void testGetUserCanSkipBouncer_whenFingerprint_nonStrongAndDisallowed() {
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(false /* isClass3Biometric */))
.thenReturn(false);
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int user = mSelectedUserInteractor.getSelectedUserId();
mKeyguardUpdateMonitor.onFingerprintAuthenticated(user, false /* isClass3Biometric */);
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isFalse();
}
@@ -1372,7 +1372,8 @@
assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(1);
assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(1);
- mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, () -> {});
+ mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, () -> {
+ });
assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(0);
assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(0);
}
@@ -1446,7 +1447,7 @@
@Test
public void testGetUserCanSkipBouncer_whenTrust() {
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int user = mSelectedUserInteractor.getSelectedUserId();
mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */,
user, 0 /* flags */, new ArrayList<>());
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
@@ -1493,7 +1494,8 @@
@Test
public void testIsUserUnlocked() {
// mUserManager will report the user as unlocked on @Before
- assertThat(mKeyguardUpdateMonitor.isUserUnlocked(KeyguardUpdateMonitor.getCurrentUser()))
+ assertThat(
+ mKeyguardUpdateMonitor.isUserUnlocked(mSelectedUserInteractor.getSelectedUserId()))
.isTrue();
// Invalid user should not be unlocked.
int randomUser = 99;
@@ -1502,7 +1504,7 @@
@Test
public void testTrustUsuallyManaged_whenTrustChanges() {
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int user = mSelectedUserInteractor.getSelectedUserId();
when(mTrustManager.isTrustUsuallyManaged(eq(user))).thenReturn(true);
mKeyguardUpdateMonitor.onTrustManagedChanged(false /* managed */, user);
assertThat(mKeyguardUpdateMonitor.isTrustUsuallyManaged(user)).isTrue();
@@ -1510,7 +1512,7 @@
@Test
public void testTrustUsuallyManaged_resetWhenUserIsRemoved() {
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int user = mSelectedUserInteractor.getSelectedUserId();
when(mTrustManager.isTrustUsuallyManaged(eq(user))).thenReturn(true);
mKeyguardUpdateMonitor.onTrustManagedChanged(false /* managed */, user);
assertThat(mKeyguardUpdateMonitor.isTrustUsuallyManaged(user)).isTrue();
@@ -1521,9 +1523,9 @@
@Test
public void testSecondaryLockscreenRequirement() {
- KeyguardUpdateMonitor.setCurrentUser(UserHandle.myUserId());
+ when(mSelectedUserInteractor.getSelectedUserId()).thenReturn(UserHandle.myUserId());
when(mUserTracker.getUserId()).thenReturn(UserHandle.myUserId());
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int user = mSelectedUserInteractor.getSelectedUserId();
String packageName = "fake.test.package";
String cls = "FakeService";
ServiceInfo serviceInfo = new ServiceInfo();
@@ -1680,7 +1682,7 @@
mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
// WHEN user loses smart unlock trust
- when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
+ when(mStrongAuthTracker.getStrongAuthForUser(mSelectedUserInteractor.getSelectedUserId()))
.thenReturn(SOME_AUTH_REQUIRED_AFTER_USER_REQUEST);
// THEN we should still listen for udfps
@@ -1724,7 +1726,7 @@
// WHEN trust is enabled (ie: via smartlock)
mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */,
- KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */, new ArrayList<>());
+ mSelectedUserInteractor.getSelectedUserId(), 0 /* flags */, new ArrayList<>());
// THEN we shouldn't listen for udfps
assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(false);
@@ -1738,7 +1740,7 @@
// WHEN face authenticated
mKeyguardUpdateMonitor.onFaceAuthenticated(
- KeyguardUpdateMonitor.getCurrentUser(), false);
+ mSelectedUserInteractor.getSelectedUserId(), false);
// THEN we shouldn't listen for udfps
assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(false);
@@ -1825,7 +1827,7 @@
public void testShowTrustGrantedMessage_onTrustGranted() {
// WHEN trust is enabled (ie: via some trust agent) with a trustGranted string
mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */,
- KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */,
+ mSelectedUserInteractor.getSelectedUserId(), 0 /* flags */,
Arrays.asList("Unlocked by wearable"));
// THEN the showTrustGrantedMessage should be called with the first message
@@ -2256,7 +2258,7 @@
mKeyguardUpdateMonitor.onTrustChanged(
true /* enabled */,
true /* newlyUnlocked */,
- getCurrentUser() /* userId */,
+ mSelectedUserInteractor.getSelectedUserId() /* userId */,
TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD /* flags */,
null /* trustGrantedMessages */);
@@ -2281,7 +2283,7 @@
mKeyguardUpdateMonitor.onTrustChanged(
true /* enabled */,
true /* newlyUnlocked */,
- getCurrentUser() /* userId */,
+ mSelectedUserInteractor.getSelectedUserId() /* userId */,
TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD /* flags */,
null /* trustGrantedMessages */);
@@ -2333,7 +2335,7 @@
mKeyguardUpdateMonitor.onTrustChanged(
true /* enabled */,
true /* newlyUnlocked */,
- getCurrentUser() /* userId */,
+ mSelectedUserInteractor.getSelectedUserId() /* userId */,
TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD
| TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE /* flags */,
null /* trustGrantedMessages */);
@@ -2362,7 +2364,7 @@
mKeyguardUpdateMonitor.onTrustChanged(
true /* enabled */,
true /* newlyUnlocked */,
- getCurrentUser() /* userId, not the current userId */,
+ mSelectedUserInteractor.getSelectedUserId() /* userId, not the current userId */,
TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER /* flags */,
null /* trustGrantedMessages */);
@@ -2388,7 +2390,7 @@
mKeyguardUpdateMonitor.onTrustChanged(
true /* enabled */,
true /* newlyUnlocked */,
- getCurrentUser() /* userId, not the current userId */,
+ mSelectedUserInteractor.getSelectedUserId() /* userId, not the current userId */,
TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER
| TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE /* flags */,
null /* trustGrantedMessages */);
@@ -2423,7 +2425,8 @@
// WHEN strong auth changes and device is in user lockdown
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
userDeviceLockDown();
- mKeyguardUpdateMonitor.notifyStrongAuthAllowedChanged(getCurrentUser());
+ mKeyguardUpdateMonitor.notifyStrongAuthAllowedChanged(
+ mSelectedUserInteractor.getSelectedUserId());
mTestableLooper.processAllMessages();
// THEN face and fingerprint listening are cancelled
@@ -2451,7 +2454,8 @@
// WHEN non-strong biometric allowed changes
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
- mKeyguardUpdateMonitor.notifyNonStrongBiometricAllowedChanged(getCurrentUser());
+ mKeyguardUpdateMonitor.notifyNonStrongBiometricAllowedChanged(
+ mSelectedUserInteractor.getSelectedUserId());
mTestableLooper.processAllMessages();
// THEN face and fingerprint listening are cancelled
@@ -2516,13 +2520,15 @@
keyguardIsVisible();
keyguardNotGoingAway();
statusBarShadeIsNotLocked();
- when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+ when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
+ true);
// WHEN the assistant is visible
mKeyguardUpdateMonitor.setAssistantVisible(true);
// THEN request unlock with keyguard dismissal
- verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ verify(mTrustManager).reportUserRequestedUnlock(
+ eq(mSelectedUserInteractor.getSelectedUserId()),
eq(true));
}
@@ -2531,7 +2537,8 @@
throws RemoteException {
// GIVEN shouldTriggerActiveUnlock
bouncerFullyVisible();
- when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+ when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
+ true);
// GIVEN active unlock triggers on biometric failures
when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -2542,7 +2549,8 @@
mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback.onAuthenticationFailed();
// ALWAYS request unlock with a keyguard dismissal
- verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ verify(mTrustManager).reportUserRequestedUnlock(
+ eq(mSelectedUserInteractor.getSelectedUserId()),
eq(true));
}
@@ -2554,7 +2562,8 @@
keyguardIsVisible();
keyguardNotGoingAway();
statusBarShadeIsNotLocked();
- when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+ when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
+ true);
// GIVEN active unlock triggers on biometric failures
when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -2566,7 +2575,8 @@
mKeyguardUpdateMonitor.mFaceAuthenticationCallback.onAuthenticationFailed();
// THEN request unlock with NO keyguard dismissal
- verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ verify(mTrustManager).reportUserRequestedUnlock(
+ eq(mSelectedUserInteractor.getSelectedUserId()),
eq(false));
}
@@ -2578,7 +2588,8 @@
keyguardIsVisible();
keyguardNotGoingAway();
statusBarShadeIsNotLocked();
- when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+ when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
+ true);
// GIVEN active unlock triggers on biometric failures
when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -2590,7 +2601,8 @@
mKeyguardUpdateMonitor.mFaceAuthenticationCallback.onAuthenticationFailed();
// THEN request unlock with a keyguard dismissal
- verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ verify(mTrustManager).reportUserRequestedUnlock(
+ eq(mSelectedUserInteractor.getSelectedUserId()),
eq(true));
}
@@ -2600,7 +2612,8 @@
// GIVEN shouldTriggerActiveUnlock
when(mAuthController.isUdfpsFingerDown()).thenReturn(false);
lockscreenBypassIsNotAllowed();
- when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+ when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
+ true);
// GIVEN active unlock triggers on biometric failures
when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -2612,7 +2625,8 @@
mKeyguardUpdateMonitor.mFaceAuthenticationCallback.onAuthenticationFailed();
// THEN request unlock with a keyguard dismissal
- verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ verify(mTrustManager).reportUserRequestedUnlock(
+ eq(mSelectedUserInteractor.getSelectedUserId()),
eq(true));
}
@@ -2701,7 +2715,8 @@
throws RemoteException {
// GIVEN shouldTriggerActiveUnlock
keyguardIsVisible();
- when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+ when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
+ true);
// GIVEN active unlock triggers on wakeup
when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -2717,7 +2732,8 @@
mTestableLooper.processAllMessages();
// THEN request unlock with a keyguard dismissal
- verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ verify(mTrustManager).reportUserRequestedUnlock(
+ eq(mSelectedUserInteractor.getSelectedUserId()),
eq(true));
}
@@ -2726,7 +2742,8 @@
throws RemoteException {
// GIVEN shouldTriggerActiveUnlock on wake from UNFOLD_DEVICE
keyguardIsVisible();
- when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+ when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
+ true);
// GIVEN active unlock triggers on wakeup
when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -2742,7 +2759,8 @@
mTestableLooper.processAllMessages();
// THEN request unlock WITHOUT a keyguard dismissal
- verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ verify(mTrustManager).reportUserRequestedUnlock(
+ eq(mSelectedUserInteractor.getSelectedUserId()),
eq(false));
}
@@ -2751,7 +2769,8 @@
throws RemoteException {
// GIVEN shouldTriggerActiveUnlock
keyguardIsVisible();
- when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+ when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
+ true);
// GIVEN active unlock triggers on wakeup
when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -2767,7 +2786,8 @@
mTestableLooper.processAllMessages();
// THEN request unlock with a keyguard dismissal
- verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ verify(mTrustManager).reportUserRequestedUnlock(
+ eq(mSelectedUserInteractor.getSelectedUserId()),
eq(true));
}
@@ -2777,7 +2797,8 @@
throws RemoteException {
// GIVEN shouldTriggerActiveUnlock on wake from UNFOLD_DEVICE
keyguardIsVisible();
- when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+ when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
+ true);
// GIVEN active unlock triggers on wakeup
when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -2793,7 +2814,8 @@
mTestableLooper.processAllMessages();
// THEN request unlock WITHOUT a keyguard dismissal
- verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ verify(mTrustManager).reportUserRequestedUnlock(
+ eq(mSelectedUserInteractor.getSelectedUserId()),
eq(false));
}
@@ -2857,27 +2879,29 @@
assertThat(captor.getValue().getWakeReason())
.isEqualTo(PowerManager.WAKE_REASON_POWER_BUTTON);
}
+
@Test
public void testFingerprintSensorProperties() throws RemoteException {
mFingerprintAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(
new ArrayList<>());
assertThat(mKeyguardUpdateMonitor.isUnlockWithFingerprintPossible(
- KeyguardUpdateMonitor.getCurrentUser())).isFalse();
+ mSelectedUserInteractor.getSelectedUserId())).isFalse();
mFingerprintAuthenticatorsRegisteredCallback
.onAllAuthenticatorsRegistered(mFingerprintSensorProperties);
verifyFingerprintAuthenticateCall();
assertThat(mKeyguardUpdateMonitor.isUnlockWithFingerprintPossible(
- KeyguardUpdateMonitor.getCurrentUser())).isTrue();
+ mSelectedUserInteractor.getSelectedUserId())).isTrue();
}
+
@Test
public void testFaceSensorProperties() throws RemoteException {
mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(new ArrayList<>());
assertThat(mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(
- KeyguardUpdateMonitor.getCurrentUser())).isFalse();
+ mSelectedUserInteractor.getSelectedUserId())).isFalse();
mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(mFaceSensorProperties);
biometricsEnabledForCurrentUser();
@@ -2885,7 +2909,7 @@
verifyFaceAuthenticateNeverCalled();
verifyFaceDetectNeverCalled();
assertThat(mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(
- KeyguardUpdateMonitor.getCurrentUser())).isTrue();
+ mSelectedUserInteractor.getSelectedUserId())).isTrue();
}
@Test
@@ -2917,13 +2941,13 @@
mKeyguardUpdateMonitor.onTrustChanged(
true /* enabled */,
true /* newlyUnlocked */,
- getCurrentUser() /* userId */,
+ mSelectedUserInteractor.getSelectedUserId() /* userId */,
TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD /* flags */,
null /* trustGrantedMessages */);
// THEN onTrustChanged is called FIRST
final InOrder inOrder = Mockito.inOrder(callback);
- inOrder.verify(callback).onTrustChanged(eq(getCurrentUser()));
+ inOrder.verify(callback).onTrustChanged(eq(mSelectedUserInteractor.getSelectedUserId()));
// AND THEN onTrustGrantedForCurrentUser callback called
inOrder.verify(callback).onTrustGrantedForCurrentUser(
@@ -3144,7 +3168,8 @@
private void mockCanBypassLockscreen(boolean canBypass) {
// force update the isFaceEnrolled cache:
- mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(getCurrentUser());
+ mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(
+ mSelectedUserInteractor.getSelectedUserId());
mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
when(mKeyguardBypassController.canBypass()).thenReturn(canBypass);
@@ -3235,22 +3260,23 @@
private void biometricsNotDisabledThroughDevicePolicyManager() {
when(mDevicePolicyManager.getKeyguardDisabledFeatures(null,
- KeyguardUpdateMonitor.getCurrentUser())).thenReturn(0);
+ mSelectedUserInteractor.getSelectedUserId())).thenReturn(0);
}
private void biometricsEnabledForCurrentUser() throws RemoteException {
- mBiometricEnabledOnKeyguardCallback.onChanged(true, KeyguardUpdateMonitor.getCurrentUser());
+ mBiometricEnabledOnKeyguardCallback.onChanged(true,
+ mSelectedUserInteractor.getSelectedUserId());
}
private void biometricsDisabledForCurrentUser() throws RemoteException {
mBiometricEnabledOnKeyguardCallback.onChanged(
false,
- KeyguardUpdateMonitor.getCurrentUser()
+ mSelectedUserInteractor.getSelectedUserId()
);
}
private void primaryAuthRequiredEncrypted() {
- when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
+ when(mStrongAuthTracker.getStrongAuthForUser(mSelectedUserInteractor.getSelectedUserId()))
.thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT);
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
}
@@ -3261,7 +3287,7 @@
}
private void primaryAuthNotRequiredByStrongAuthTracker() {
- when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
+ when(mStrongAuthTracker.getStrongAuthForUser(mSelectedUserInteractor.getSelectedUserId()))
.thenReturn(0);
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
}
@@ -3270,7 +3296,7 @@
mKeyguardUpdateMonitor.onTrustChanged(
false,
false,
- KeyguardUpdateMonitor.getCurrentUser(),
+ mSelectedUserInteractor.getSelectedUserId(),
-1,
new ArrayList<>()
);
@@ -3435,7 +3461,7 @@
protected TestableKeyguardUpdateMonitor(Context context) {
super(context, mUserTracker,
TestableLooper.get(KeyguardUpdateMonitorTest.this).getLooper(),
- mBroadcastDispatcher, mSecureSettings, mDumpManager,
+ mBroadcastDispatcher, mDumpManager,
mBackgroundExecutor, mMainExecutor,
mStatusBarStateController, mLockPatternUtils,
mAuthController, mTelephonyListenerManager,
@@ -3447,7 +3473,7 @@
mFaceWakeUpTriggersConfig, mDevicePostureController,
Optional.of(mInteractiveToAuthProvider),
mTaskStackChangeListeners, mActivityTaskManager, mDisplayTracker,
- mWakefulness);
+ mWakefulness, mSelectedUserInteractor);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt
new file mode 100644
index 0000000..b1421b2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt
@@ -0,0 +1,162 @@
+package com.android.systemui
+
+import android.graphics.Path
+import android.graphics.Rect
+import android.graphics.RectF
+import android.hardware.camera2.CameraManager
+import android.testing.AndroidTestingRunner
+import android.util.PathParser
+import androidx.test.filters.SmallTest
+import com.android.systemui.res.R
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.withArgCaptor
+import java.util.concurrent.Executor
+import kotlin.math.roundToInt
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class CameraAvailabilityListenerTest : SysuiTestCase() {
+ companion object {
+ const val EXCLUDED_PKG = "test.excluded.package"
+ const val CAMERA_ID_FRONT = "0"
+ const val CAMERA_ID_INNER = "1"
+ const val PROTECTION_PATH_STRING_FRONT = "M 50,50 a 20,20 0 1 0 40,0 a 20,20 0 1 0 -40,0 Z"
+ const val PROTECTION_PATH_STRING_INNER = "M 40,40 a 10,10 0 1 0 20,0 a 10,10 0 1 0 -20,0 Z"
+ val PATH_RECT_FRONT = rectFromPath(pathFromString(PROTECTION_PATH_STRING_FRONT))
+ val PATH_RECT_INNER = rectFromPath(pathFromString(PROTECTION_PATH_STRING_INNER))
+
+ private fun pathFromString(pathString: String): Path {
+ val spec = pathString.trim()
+ val p: Path
+ try {
+ p = PathParser.createPathFromPathData(spec)
+ } catch (e: Throwable) {
+ throw IllegalArgumentException("Invalid protection path", e)
+ }
+
+ return p
+ }
+
+ private fun rectFromPath(path: Path): Rect {
+ val computed = RectF()
+ path.computeBounds(computed)
+ return Rect(
+ computed.left.roundToInt(),
+ computed.top.roundToInt(),
+ computed.right.roundToInt(),
+ computed.bottom.roundToInt()
+ )
+ }
+ }
+
+ @Mock private lateinit var cameraManager: CameraManager
+ @Mock
+ private lateinit var cameraTransitionCb: CameraAvailabilityListener.CameraTransitionCallback
+ private lateinit var cameraAvailabilityListener: CameraAvailabilityListener
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ context
+ .getOrCreateTestableResources()
+ .addOverride(R.string.config_cameraProtectionExcludedPackages, EXCLUDED_PKG)
+ context
+ .getOrCreateTestableResources()
+ .addOverride(R.string.config_protectedCameraId, CAMERA_ID_FRONT)
+ context
+ .getOrCreateTestableResources()
+ .addOverride(
+ R.string.config_frontBuiltInDisplayCutoutProtection,
+ PROTECTION_PATH_STRING_FRONT
+ )
+ context
+ .getOrCreateTestableResources()
+ .addOverride(R.string.config_protectedInnerCameraId, CAMERA_ID_INNER)
+ context
+ .getOrCreateTestableResources()
+ .addOverride(
+ R.string.config_innerBuiltInDisplayCutoutProtection,
+ PROTECTION_PATH_STRING_INNER
+ )
+
+ context.addMockSystemService(CameraManager::class.java, cameraManager)
+
+ cameraAvailabilityListener =
+ CameraAvailabilityListener.Factory.build(context, context.mainExecutor)
+ }
+
+ @Test
+ fun testFrontCamera() {
+ var path: Path? = null
+ var rect: Rect? = null
+ val callback =
+ object : CameraAvailabilityListener.CameraTransitionCallback {
+ override fun onApplyCameraProtection(protectionPath: Path, bounds: Rect) {
+ path = protectionPath
+ rect = bounds
+ }
+
+ override fun onHideCameraProtection() {}
+ }
+
+ cameraAvailabilityListener.addTransitionCallback(callback)
+ cameraAvailabilityListener.startListening()
+
+ val callbackCaptor = withArgCaptor {
+ verify(cameraManager).registerAvailabilityCallback(any(Executor::class.java), capture())
+ }
+
+ callbackCaptor.onCameraOpened(CAMERA_ID_FRONT, "")
+ assertNotNull(path)
+ assertEquals(PATH_RECT_FRONT, rect)
+ }
+
+ @Test
+ fun testInnerCamera() {
+ var path: Path? = null
+ var rect: Rect? = null
+ val callback =
+ object : CameraAvailabilityListener.CameraTransitionCallback {
+ override fun onApplyCameraProtection(protectionPath: Path, bounds: Rect) {
+ path = protectionPath
+ rect = bounds
+ }
+
+ override fun onHideCameraProtection() {}
+ }
+
+ cameraAvailabilityListener.addTransitionCallback(callback)
+ cameraAvailabilityListener.startListening()
+
+ val callbackCaptor = withArgCaptor {
+ verify(cameraManager).registerAvailabilityCallback(any(Executor::class.java), capture())
+ }
+
+ callbackCaptor.onCameraOpened(CAMERA_ID_INNER, "")
+ assertNotNull(path)
+ assertEquals(PATH_RECT_INNER, rect)
+ }
+
+ @Test
+ fun testExcludedPackage() {
+ cameraAvailabilityListener.addTransitionCallback(cameraTransitionCb)
+ cameraAvailabilityListener.startListening()
+
+ val callbackCaptor = withArgCaptor {
+ verify(cameraManager).registerAvailabilityCallback(any(Executor::class.java), capture())
+ }
+ callbackCaptor.onCameraOpened(CAMERA_ID_FRONT, EXCLUDED_PKG)
+
+ verify(cameraTransitionCb, never())
+ .onApplyCameraProtection(any(Path::class.java), any(Rect::class.java))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
index 5346db1..daa6070 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
@@ -19,8 +19,7 @@
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
-import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
-import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
import static com.google.common.truth.Truth.assertThat;
@@ -90,7 +89,7 @@
@Before
public void setUp() throws Exception {
- setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
+ setFlagDefaults(mSetFlagsRule);
MockitoAnnotations.initMocks(this);
mContextWrapper = new ContextWrapper(mContext) {
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
index 3b2ea0f..5666435 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
@@ -16,8 +16,7 @@
package com.android.systemui.accessibility.floatingmenu;
-import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
-import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -59,7 +58,7 @@
@Before
public void setUp() throws Exception {
- setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
+ setFlagDefaults(mSetFlagsRule);
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
mock(SecureSettings.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
index 76a3153..ec6ec63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
@@ -16,8 +16,7 @@
package com.android.systemui.accessibility.floatingmenu;
-import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
-import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
import static com.google.common.truth.Truth.assertThat;
@@ -78,7 +77,7 @@
@Before
public void setUp() throws Exception {
- setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
+ setFlagDefaults(mSetFlagsRule);
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
stubWindowManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
index 83bcd8d..e8192c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
@@ -16,8 +16,7 @@
package com.android.systemui.accessibility.floatingmenu;
-import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
-import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
import static com.google.common.truth.Truth.assertThat;
@@ -29,8 +28,8 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.res.R;
import org.junit.Before;
import org.junit.Test;
@@ -46,7 +45,7 @@
@Before
public void setUp() throws Exception {
- setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
+ setFlagDefaults(mSetFlagsRule);
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
mMenuViewAppearance = new MenuViewAppearance(mContext, windowManager);
mMenuEduTooltipView = new MenuEduTooltipView(mContext, mMenuViewAppearance);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
index e01f1b7..62cb9a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
@@ -19,8 +19,7 @@
import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS;
import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
-import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
-import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
import static com.google.common.truth.Truth.assertThat;
@@ -40,8 +39,8 @@
import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate;
import androidx.test.filters.SmallTest;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.res.R;
import com.android.systemui.util.settings.SecureSettings;
import org.junit.Before;
@@ -75,7 +74,7 @@
@Before
public void setUp() {
- setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
+ setFlagDefaults(mSetFlagsRule);
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
stubWindowManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index a88ee10..3248753 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -18,8 +18,7 @@
import static android.view.View.OVER_SCROLL_NEVER;
-import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
-import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
import static com.google.common.truth.Truth.assertThat;
@@ -83,7 +82,7 @@
@Before
public void setUp() throws Exception {
- setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
+ setFlagDefaults(mSetFlagsRule);
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
mock(SecureSettings.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
index 41e5c20..03a4ba7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
@@ -19,8 +19,7 @@
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.systemBars;
-import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
-import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
@@ -76,7 +75,7 @@
@Before
public void setUp() throws Exception {
- setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
+ setFlagDefaults(mSetFlagsRule);
final WindowManager wm = mContext.getSystemService(WindowManager.class);
doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
mWindowManager).getMaximumWindowMetrics();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index b0776c9..df7d1b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -22,9 +22,8 @@
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
-import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
import static com.android.systemui.accessibility.floatingmenu.MenuViewLayer.LayerIndex;
-import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
import static com.google.common.truth.Truth.assertThat;
@@ -113,7 +112,7 @@
@Before
public void setUp() throws Exception {
- setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
+ setFlagDefaults(mSetFlagsRule);
final Rect mDisplayBounds = new Rect();
mDisplayBounds.set(/* left= */ 0, /* top= */ 0, DISPLAY_WINDOW_WIDTH,
DISPLAY_WINDOW_HEIGHT);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
index ac2bfaf..b9fd5d0f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -18,8 +18,7 @@
import static android.app.UiModeManager.MODE_NIGHT_YES;
-import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
-import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
import static com.google.common.truth.Truth.assertThat;
@@ -70,7 +69,7 @@
@Before
public void setUp() throws Exception {
- setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
+ setFlagDefaults(mSetFlagsRule);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mNightMode = mUiModeManager.getNightMode();
mUiModeManager.setNightMode(MODE_NIGHT_YES);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/utils/FlagUtils.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/utils/FlagUtils.java
new file mode 100644
index 0000000..2975549
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/utils/FlagUtils.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.utils;
+
+import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import com.android.systemui.Flags;
+
+public class FlagUtils {
+ /**
+ * Populates a setFlagsRule with every SystemUI a11y feature flag.
+ * This function should be updated when new flags are added.
+ *
+ * @param setFlagsRule set flags rule from the test environment.
+ */
+ public static void setFlagDefaults(SetFlagsRule setFlagsRule) {
+ setFlagDefault(setFlagsRule, Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconControllerTest.kt
deleted file mode 100644
index 215d635..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconControllerTest.kt
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.content.Context
-import android.hardware.biometrics.SensorProperties
-import android.hardware.fingerprint.FingerprintManager
-import android.hardware.fingerprint.FingerprintSensorProperties
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
-import android.view.ViewGroup.LayoutParams
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.airbnb.lottie.LottieAnimationView
-import com.android.systemui.res.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.`when` as whenEver
-import org.mockito.junit.MockitoJUnit
-
-private const val SENSOR_ID = 1
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class AuthBiometricFingerprintIconControllerTest : SysuiTestCase() {
-
- @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
-
- @Mock private lateinit var iconView: LottieAnimationView
- @Mock private lateinit var iconViewOverlay: LottieAnimationView
- @Mock private lateinit var layoutParam: LayoutParams
- @Mock private lateinit var fingerprintManager: FingerprintManager
-
- private lateinit var controller: AuthBiometricFingerprintIconController
-
- @Before
- fun setUp() {
- context.addMockSystemService(Context.FINGERPRINT_SERVICE, fingerprintManager)
- whenEver(iconView.layoutParams).thenReturn(layoutParam)
- whenEver(iconViewOverlay.layoutParams).thenReturn(layoutParam)
- }
-
- @Test
- fun testIconContentDescription_SfpsDevice() {
- setupFingerprintSensorProperties(FingerprintSensorProperties.TYPE_POWER_BUTTON)
- controller = AuthBiometricFingerprintIconController(context, iconView, iconViewOverlay)
-
- assertThat(controller.getIconContentDescription(BiometricState.STATE_AUTHENTICATING))
- .isEqualTo(
- context.resources.getString(
- R.string.security_settings_sfps_enroll_find_sensor_message
- )
- )
- }
-
- @Test
- fun testIconContentDescription_NonSfpsDevice() {
- setupFingerprintSensorProperties(FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)
- controller = AuthBiometricFingerprintIconController(context, iconView, iconViewOverlay)
-
- assertThat(controller.getIconContentDescription(BiometricState.STATE_AUTHENTICATING))
- .isEqualTo(context.resources.getString(R.string.fingerprint_dialog_touch_sensor))
- }
-
- private fun setupFingerprintSensorProperties(sensorType: Int) {
- whenEver(fingerprintManager.sensorPropertiesInternal)
- .thenReturn(
- listOf(
- FingerprintSensorPropertiesInternal(
- SENSOR_ID,
- SensorProperties.STRENGTH_STRONG,
- 5 /* maxEnrollmentsPerUser */,
- listOf() /* componentInfo */,
- sensorType,
- true /* halControlsIllumination */,
- true /* resetLockoutRequiresHardwareAuthToken */,
- listOf() /* sensorLocations */
- )
- )
- )
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
index d68a313..8c26776 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
@@ -72,6 +72,7 @@
runCurrent()
// WHEN shade expands
+ shadeRepository.setLegacyShadeTracking(true)
shadeRepository.setLegacyShadeExpansion(.5f)
runCurrent()
@@ -108,6 +109,7 @@
// WHEN detector is disabled and shade opens
detector.disable()
+ shadeRepository.setLegacyShadeTracking(true)
shadeRepository.setLegacyShadeExpansion(.5f)
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
index 9f24a9f..15633d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
@@ -30,6 +30,7 @@
internal fun fingerprintSensorPropertiesInternal(
ids: List<Int> = listOf(0),
strong: Boolean = true,
+ sensorType: Int = FingerprintSensorProperties.TYPE_REAR
): List<FingerprintSensorPropertiesInternal> {
val componentInfo =
listOf(
@@ -54,7 +55,7 @@
if (strong) SensorProperties.STRENGTH_STRONG else SensorProperties.STRENGTH_WEAK,
5 /* maxEnrollmentsPerUser */,
componentInfo,
- FingerprintSensorProperties.TYPE_REAR,
+ sensorType,
false /* resetLockoutRequiresHardwareAuthToken */
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index ebe13fe..c5f16aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -36,7 +36,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
@@ -46,12 +45,14 @@
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -111,6 +112,7 @@
@Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
@Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
@Mock private lateinit var udfpsUtils: UdfpsUtils
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock private lateinit var udfpsKeyguardAccessibilityDelegate:
UdfpsKeyguardAccessibilityDelegate
@Mock private lateinit var udfpsKeyguardViewModels: Provider<UdfpsKeyguardViewModels>
@@ -163,6 +165,7 @@
isDebuggable,
udfpsKeyguardAccessibilityDelegate,
udfpsKeyguardViewModels,
+ mSelectedUserInteractor,
)
block()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index dcb5398..f32e1a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -102,6 +102,7 @@
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.concurrency.FakeExecution;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -214,6 +215,8 @@
private UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
@Mock
private Provider<UdfpsKeyguardViewModels> mUdfpsKeyguardViewModels;
+ @Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
// Capture listeners so that they can be used to send events
@Captor
@@ -326,7 +329,8 @@
mInputManager,
mock(KeyguardFaceAuthInteractor.class),
mUdfpsKeyguardAccessibilityDelegate,
- mUdfpsKeyguardViewModels
+ mUdfpsKeyguardViewModels,
+ mSelectedUserInteractor
);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
index e512adc..2c4e136 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
@@ -40,6 +40,7 @@
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.concurrency.DelayableExecutor;
import org.junit.Before;
@@ -69,6 +70,7 @@
protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
protected @Mock AlternateBouncerInteractor mAlternateBouncerInteractor;
protected @Mock UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
+ protected @Mock SelectedUserInteractor mSelectedUserInteractor;
protected FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@@ -142,7 +144,8 @@
mFeatureFlags,
mPrimaryBouncerInteractor,
mAlternateBouncerInteractor,
- mUdfpsKeyguardAccessibilityDelegate);
+ mUdfpsKeyguardAccessibilityDelegate,
+ mSelectedUserInteractor);
return controller;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index 02ee53879..97dada2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -96,6 +96,7 @@
mKeyguardUpdateMonitor,
FakeTrustRepository(),
testScope.backgroundScope,
+ mSelectedUserInteractor,
)
mAlternateBouncerInteractor =
AlternateBouncerInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
index 6b9c34b..bbf471c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
@@ -24,6 +24,7 @@
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
@@ -55,6 +56,7 @@
@Mock private lateinit var udfpsOverlayParams: UdfpsOverlayParams
@Mock private lateinit var overlayBounds: Rect
+ @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
private lateinit var underTest: UdfpsOverlayInteractor
@@ -104,7 +106,12 @@
}
private fun createUdpfsOverlayInteractor() {
- underTest = UdfpsOverlayInteractor(authController, testScope.backgroundScope)
+ underTest =
+ UdfpsOverlayInteractor(
+ authController,
+ selectedUserInteractor,
+ testScope.backgroundScope
+ )
testScope.runCurrent()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModelTest.kt
deleted file mode 100644
index fd86486..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModelTest.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-package com.android.systemui.biometrics.ui.viewmodel
-
-import android.content.res.Configuration
-import androidx.test.filters.SmallTest
-import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
-import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
-import com.android.systemui.biometrics.data.repository.FakePromptRepository
-import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
-import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
-import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
-import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl
-import com.android.systemui.biometrics.shared.model.FingerprintSensorType
-import com.android.systemui.biometrics.shared.model.SensorStrength
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.display.data.repository.FakeDisplayRepository
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.Mock
-import org.mockito.junit.MockitoJUnit
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(JUnit4::class)
-class PromptFingerprintIconViewModelTest : SysuiTestCase() {
-
- @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
-
- @Mock private lateinit var lockPatternUtils: LockPatternUtils
-
- private lateinit var displayRepository: FakeDisplayRepository
- private lateinit var fingerprintRepository: FakeFingerprintPropertyRepository
- private lateinit var promptRepository: FakePromptRepository
- private lateinit var displayStateRepository: FakeDisplayStateRepository
-
- private val testScope = TestScope(StandardTestDispatcher())
- private val fakeExecutor = FakeExecutor(FakeSystemClock())
-
- private lateinit var promptSelectorInteractor: PromptSelectorInteractor
- private lateinit var displayStateInteractor: DisplayStateInteractor
- private lateinit var viewModel: PromptFingerprintIconViewModel
-
- @Before
- fun setup() {
- displayRepository = FakeDisplayRepository()
- fingerprintRepository = FakeFingerprintPropertyRepository()
- promptRepository = FakePromptRepository()
- displayStateRepository = FakeDisplayStateRepository()
-
- promptSelectorInteractor =
- PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
- displayStateInteractor =
- DisplayStateInteractorImpl(
- testScope.backgroundScope,
- mContext,
- fakeExecutor,
- displayStateRepository,
- displayRepository,
- )
- viewModel = PromptFingerprintIconViewModel(displayStateInteractor, promptSelectorInteractor)
- }
-
- @Test
- fun sfpsIconUpdates_onConfigurationChanged() {
- testScope.runTest {
- runCurrent()
- configureFingerprintPropertyRepository(FingerprintSensorType.POWER_BUTTON)
- val testConfig = Configuration()
- val folded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP - 1
- val unfolded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP + 1
- val currentIcon = collectLastValue(viewModel.iconAsset)
-
- testConfig.smallestScreenWidthDp = folded
- viewModel.onConfigurationChanged(testConfig)
- val foldedIcon = currentIcon()
-
- testConfig.smallestScreenWidthDp = unfolded
- viewModel.onConfigurationChanged(testConfig)
- val unfoldedIcon = currentIcon()
-
- assertThat(foldedIcon).isNotEqualTo(unfoldedIcon)
- }
- }
-
- private fun configureFingerprintPropertyRepository(sensorType: FingerprintSensorType) {
- fingerprintRepository.setProperties(0, SensorStrength.STRONG, sensorType, mapOf())
- }
-}
-
-internal const val INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP = 600
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index ca6df40..b695a0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -16,8 +16,10 @@
package com.android.systemui.biometrics.ui.viewmodel
+import android.content.res.Configuration
import android.hardware.biometrics.PromptInfo
import android.hardware.face.FaceSensorPropertiesInternal
+import android.hardware.fingerprint.FingerprintSensorProperties
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
@@ -36,12 +38,15 @@
import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal
import com.android.systemui.biometrics.shared.model.BiometricModalities
import com.android.systemui.biometrics.shared.model.BiometricModality
-import com.android.systemui.biometrics.ui.binder.Spaghetti.BiometricState
+import com.android.systemui.biometrics.shared.model.DisplayRotation
+import com.android.systemui.biometrics.shared.model.toSensorStrength
+import com.android.systemui.biometrics.shared.model.toSensorType
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.display.data.repository.FakeDisplayRepository
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
+import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
@@ -66,6 +71,7 @@
private const val USER_ID = 4
private const val CHALLENGE = 2L
+private const val DELAY = 1000L
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -88,11 +94,22 @@
private lateinit var selector: PromptSelectorInteractor
private lateinit var viewModel: PromptViewModel
+ private lateinit var iconViewModel: PromptIconViewModel
private val featureFlags = FakeFeatureFlags()
@Before
fun setup() {
fingerprintRepository = FakeFingerprintPropertyRepository()
+ testCase.fingerprint?.let {
+ fingerprintRepository.setProperties(
+ it.sensorId,
+ it.sensorStrength.toSensorStrength(),
+ it.sensorType.toSensorType(),
+ it.allLocations.associateBy { sensorLocationInternal ->
+ sensorLocationInternal.displayId
+ }
+ )
+ }
promptRepository = FakePromptRepository()
displayStateRepository = FakeDisplayStateRepository()
displayRepository = FakeDisplayRepository()
@@ -110,6 +127,7 @@
viewModel =
PromptViewModel(displayStateInteractor, selector, vibrator, mContext, featureFlags)
+ iconViewModel = viewModel.iconViewModel
featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
}
@@ -123,7 +141,6 @@
val modalities by collectLastValue(viewModel.modalities)
val message by collectLastValue(viewModel.message)
val size by collectLastValue(viewModel.size)
- val legacyState by collectLastValue(viewModel.legacyState)
assertThat(authenticating).isFalse()
assertThat(authenticated?.isNotAuthenticated).isTrue()
@@ -133,7 +150,6 @@
}
assertThat(message).isEqualTo(PromptMessage.Empty)
assertThat(size).isEqualTo(expectedSize)
- assertThat(legacyState).isEqualTo(BiometricState.STATE_IDLE)
val startMessage = "here we go"
viewModel.showAuthenticating(startMessage, isRetry = false)
@@ -143,7 +159,6 @@
assertThat(authenticated?.isNotAuthenticated).isTrue()
assertThat(size).isEqualTo(expectedSize)
assertButtonsVisible(negative = expectedSize != PromptSize.SMALL)
- assertThat(legacyState).isEqualTo(BiometricState.STATE_AUTHENTICATING)
}
@Test
@@ -205,6 +220,472 @@
assertThat(currentConstant).isEqualTo(HapticFeedbackConstants.REJECT)
}
+ @Test
+ fun start_idle_and_show_authenticating_iconUpdate() =
+ runGenericTest(doNotStart = true) {
+ val currentRotation by collectLastValue(displayStateInteractor.currentRotation)
+ val iconAsset by collectLastValue(iconViewModel.iconAsset)
+ val iconContentDescriptionId by collectLastValue(iconViewModel.contentDescriptionId)
+ val shouldAnimateIconView by collectLastValue(iconViewModel.shouldAnimateIconView)
+
+ val forceExplicitFlow = testCase.isCoex && testCase.authenticatedByFingerprint
+ if (forceExplicitFlow) {
+ viewModel.ensureFingerprintHasStarted(isDelayed = true)
+ }
+
+ val startMessage = "here we go"
+ viewModel.showAuthenticating(startMessage, isRetry = false)
+
+ if (testCase.isFingerprintOnly) {
+ val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
+ val shouldAnimateIconOverlay by
+ collectLastValue(iconViewModel.shouldAnimateIconOverlay)
+
+ if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
+ val expectedOverlayAsset =
+ when (currentRotation) {
+ DisplayRotation.ROTATION_0 ->
+ R.raw.biometricprompt_fingerprint_to_error_landscape
+ DisplayRotation.ROTATION_90 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft
+ DisplayRotation.ROTATION_180 ->
+ R.raw.biometricprompt_fingerprint_to_error_landscape
+ DisplayRotation.ROTATION_270 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright
+ else -> throw Exception("invalid rotation")
+ }
+ assertThat(iconOverlayAsset).isEqualTo(expectedOverlayAsset)
+ assertThat(iconContentDescriptionId)
+ .isEqualTo(R.string.security_settings_sfps_enroll_find_sensor_message)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
+ } else {
+ assertThat(iconAsset)
+ .isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_error_lottie)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
+ assertThat(iconContentDescriptionId)
+ .isEqualTo(R.string.fingerprint_dialog_touch_sensor)
+ assertThat(shouldAnimateIconView).isEqualTo(false)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
+ }
+ }
+
+ if (testCase.isFaceOnly) {
+ val shouldRepeatAnimation by collectLastValue(iconViewModel.shouldRepeatAnimation)
+ val shouldPulseAnimation by collectLastValue(iconViewModel.shouldPulseAnimation)
+ val lastPulseLightToDark by collectLastValue(iconViewModel.lastPulseLightToDark)
+
+ val expectedIconAsset =
+ if (shouldPulseAnimation!!) {
+ if (lastPulseLightToDark!!) {
+ R.drawable.face_dialog_pulse_dark_to_light
+ } else {
+ R.drawable.face_dialog_pulse_light_to_dark
+ }
+ } else {
+ R.drawable.face_dialog_pulse_dark_to_light
+ }
+ assertThat(iconAsset).isEqualTo(expectedIconAsset)
+ assertThat(iconContentDescriptionId)
+ .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticating)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldRepeatAnimation).isEqualTo(true)
+ }
+
+ if (testCase.isCoex) {
+ if (testCase.confirmationRequested || forceExplicitFlow) {
+ // explicit flow
+ val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
+ val shouldAnimateIconOverlay by
+ collectLastValue(iconViewModel.shouldAnimateIconOverlay)
+
+ // TODO: Update when SFPS co-ex is implemented
+ if (testCase.sensorType != FingerprintSensorProperties.TYPE_POWER_BUTTON) {
+ assertThat(iconAsset)
+ .isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_error_lottie)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
+ assertThat(iconContentDescriptionId)
+ .isEqualTo(R.string.fingerprint_dialog_touch_sensor)
+ assertThat(shouldAnimateIconView).isEqualTo(false)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
+ }
+ } else {
+ // implicit flow
+ val shouldRepeatAnimation by
+ collectLastValue(iconViewModel.shouldRepeatAnimation)
+ val shouldPulseAnimation by collectLastValue(iconViewModel.shouldPulseAnimation)
+ val lastPulseLightToDark by collectLastValue(iconViewModel.lastPulseLightToDark)
+
+ val expectedIconAsset =
+ if (shouldPulseAnimation!!) {
+ if (lastPulseLightToDark!!) {
+ R.drawable.face_dialog_pulse_dark_to_light
+ } else {
+ R.drawable.face_dialog_pulse_light_to_dark
+ }
+ } else {
+ R.drawable.face_dialog_pulse_dark_to_light
+ }
+ assertThat(iconAsset).isEqualTo(expectedIconAsset)
+ assertThat(iconContentDescriptionId)
+ .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticating)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldRepeatAnimation).isEqualTo(true)
+ }
+ }
+ }
+
+ @Test
+ fun start_authenticating_show_and_clear_error_iconUpdate() = runGenericTest {
+ val currentRotation by collectLastValue(displayStateInteractor.currentRotation)
+
+ val iconAsset by collectLastValue(iconViewModel.iconAsset)
+ val iconContentDescriptionId by collectLastValue(iconViewModel.contentDescriptionId)
+ val shouldAnimateIconView by collectLastValue(iconViewModel.shouldAnimateIconView)
+
+ val forceExplicitFlow = testCase.isCoex && testCase.authenticatedByFingerprint
+ if (forceExplicitFlow) {
+ viewModel.ensureFingerprintHasStarted(isDelayed = true)
+ }
+
+ val errorJob = launch {
+ viewModel.showTemporaryError(
+ "so sad",
+ messageAfterError = "",
+ authenticateAfterError = testCase.isFingerprintOnly || testCase.isCoex,
+ )
+ // Usually done by binder
+ iconViewModel.setPreviousIconWasError(true)
+ iconViewModel.setPreviousIconOverlayWasError(true)
+ }
+
+ if (testCase.isFingerprintOnly) {
+ val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
+ val shouldAnimateIconOverlay by collectLastValue(iconViewModel.shouldAnimateIconOverlay)
+
+ if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
+ val expectedOverlayAsset =
+ when (currentRotation) {
+ DisplayRotation.ROTATION_0 ->
+ R.raw.biometricprompt_fingerprint_to_error_landscape
+ DisplayRotation.ROTATION_90 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft
+ DisplayRotation.ROTATION_180 ->
+ R.raw.biometricprompt_fingerprint_to_error_landscape
+ DisplayRotation.ROTATION_270 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright
+ else -> throw Exception("invalid rotation")
+ }
+ assertThat(iconOverlayAsset).isEqualTo(expectedOverlayAsset)
+ assertThat(iconContentDescriptionId).isEqualTo(R.string.biometric_dialog_try_again)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(true)
+ } else {
+ assertThat(iconAsset)
+ .isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_error_lottie)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
+ assertThat(iconContentDescriptionId).isEqualTo(R.string.biometric_dialog_try_again)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
+ }
+
+ // Clear error, restart authenticating
+ errorJob.join()
+
+ if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
+ val expectedOverlayAsset =
+ when (currentRotation) {
+ DisplayRotation.ROTATION_0 ->
+ R.raw.biometricprompt_symbol_error_to_fingerprint_landscape
+ DisplayRotation.ROTATION_90 ->
+ R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_topleft
+ DisplayRotation.ROTATION_180 ->
+ R.raw.biometricprompt_symbol_error_to_fingerprint_landscape
+ DisplayRotation.ROTATION_270 ->
+ R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_bottomright
+ else -> throw Exception("invalid rotation")
+ }
+ assertThat(iconOverlayAsset).isEqualTo(expectedOverlayAsset)
+ assertThat(iconContentDescriptionId)
+ .isEqualTo(R.string.security_settings_sfps_enroll_find_sensor_message)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(true)
+ } else {
+ assertThat(iconAsset)
+ .isEqualTo(R.raw.fingerprint_dialogue_error_to_fingerprint_lottie)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
+ assertThat(iconContentDescriptionId)
+ .isEqualTo(R.string.fingerprint_dialog_touch_sensor)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
+ }
+ }
+
+ if (testCase.isFaceOnly) {
+ val shouldRepeatAnimation by collectLastValue(iconViewModel.shouldRepeatAnimation)
+ val shouldPulseAnimation by collectLastValue(iconViewModel.shouldPulseAnimation)
+
+ assertThat(shouldPulseAnimation!!).isEqualTo(false)
+ assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_dark_to_error)
+ assertThat(iconContentDescriptionId).isEqualTo(R.string.keyguard_face_failed)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldRepeatAnimation).isEqualTo(false)
+
+ // Clear error, go to idle
+ errorJob.join()
+
+ assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_error_to_idle)
+ assertThat(iconContentDescriptionId)
+ .isEqualTo(R.string.biometric_dialog_face_icon_description_idle)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldRepeatAnimation).isEqualTo(false)
+ }
+
+ if (testCase.isCoex) {
+ val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
+ val shouldAnimateIconOverlay by collectLastValue(iconViewModel.shouldAnimateIconOverlay)
+
+ // TODO: Update when SFPS co-ex is implemented
+ if (testCase.sensorType != FingerprintSensorProperties.TYPE_POWER_BUTTON) {
+ assertThat(iconAsset)
+ .isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_error_lottie)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
+ assertThat(iconContentDescriptionId).isEqualTo(R.string.biometric_dialog_try_again)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
+ }
+
+ // Clear error, restart authenticating
+ errorJob.join()
+
+ if (testCase.sensorType != FingerprintSensorProperties.TYPE_POWER_BUTTON) {
+ assertThat(iconAsset)
+ .isEqualTo(R.raw.fingerprint_dialogue_error_to_fingerprint_lottie)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
+ assertThat(iconContentDescriptionId)
+ .isEqualTo(R.string.fingerprint_dialog_touch_sensor)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
+ }
+ }
+ }
+
+ @Test
+ fun shows_authenticated_no_errors_no_confirmation_required_iconUpdate() = runGenericTest {
+ if (!testCase.confirmationRequested) {
+ val currentRotation by collectLastValue(displayStateInteractor.currentRotation)
+
+ val iconAsset by collectLastValue(iconViewModel.iconAsset)
+ val iconContentDescriptionId by collectLastValue(iconViewModel.contentDescriptionId)
+ val shouldAnimateIconView by collectLastValue(iconViewModel.shouldAnimateIconView)
+
+ viewModel.showAuthenticated(
+ modality = testCase.authenticatedModality,
+ dismissAfterDelay = DELAY
+ )
+
+ if (testCase.isFingerprintOnly) {
+ val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
+ val shouldAnimateIconOverlay by
+ collectLastValue(iconViewModel.shouldAnimateIconOverlay)
+
+ if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
+ val expectedOverlayAsset =
+ when (currentRotation) {
+ DisplayRotation.ROTATION_0 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
+ DisplayRotation.ROTATION_90 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_topleft
+ DisplayRotation.ROTATION_180 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
+ DisplayRotation.ROTATION_270 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_bottomright
+ else -> throw Exception("invalid rotation")
+ }
+ assertThat(iconOverlayAsset).isEqualTo(expectedOverlayAsset)
+ assertThat(iconContentDescriptionId)
+ .isEqualTo(R.string.security_settings_sfps_enroll_find_sensor_message)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(true)
+ } else {
+ val isAuthenticated by collectLastValue(viewModel.isAuthenticated)
+ assertThat(iconAsset)
+ .isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_success_lottie)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
+ assertThat(iconContentDescriptionId)
+ .isEqualTo(R.string.fingerprint_dialog_touch_sensor)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
+ }
+ }
+
+ // If co-ex, using implicit flow (explicit flow always requires confirmation)
+ if (testCase.isFaceOnly || testCase.isCoex) {
+ val shouldRepeatAnimation by collectLastValue(iconViewModel.shouldRepeatAnimation)
+ val shouldPulseAnimation by collectLastValue(iconViewModel.shouldPulseAnimation)
+
+ assertThat(shouldPulseAnimation!!).isEqualTo(false)
+ assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_dark_to_checkmark)
+ assertThat(iconContentDescriptionId)
+ .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticated)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldRepeatAnimation).isEqualTo(false)
+ }
+ }
+ }
+
+ @Test
+ fun shows_pending_confirmation_iconUpdate() = runGenericTest {
+ if (
+ (testCase.isFaceOnly || testCase.isCoex) &&
+ testCase.authenticatedByFace &&
+ testCase.confirmationRequested
+ ) {
+ val iconAsset by collectLastValue(iconViewModel.iconAsset)
+ val iconContentDescriptionId by collectLastValue(iconViewModel.contentDescriptionId)
+ val shouldAnimateIconView by collectLastValue(iconViewModel.shouldAnimateIconView)
+
+ viewModel.showAuthenticated(
+ modality = testCase.authenticatedModality,
+ dismissAfterDelay = DELAY
+ )
+
+ if (testCase.isFaceOnly) {
+ val shouldRepeatAnimation by collectLastValue(iconViewModel.shouldRepeatAnimation)
+ val shouldPulseAnimation by collectLastValue(iconViewModel.shouldPulseAnimation)
+
+ assertThat(shouldPulseAnimation!!).isEqualTo(false)
+ assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_wink_from_dark)
+ assertThat(iconContentDescriptionId)
+ .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticated)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldRepeatAnimation).isEqualTo(false)
+ }
+
+ // explicit flow because confirmation requested
+ if (testCase.isCoex) {
+ val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
+ val shouldAnimateIconOverlay by
+ collectLastValue(iconViewModel.shouldAnimateIconOverlay)
+
+ // TODO: Update when SFPS co-ex is implemented
+ if (testCase.sensorType != FingerprintSensorProperties.TYPE_POWER_BUTTON) {
+ assertThat(iconAsset)
+ .isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_unlock_lottie)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
+ assertThat(iconContentDescriptionId)
+ .isEqualTo(R.string.fingerprint_dialog_authenticated_confirmation)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
+ }
+ }
+ }
+ }
+
+ @Test
+ fun shows_authenticated_explicitly_confirmed_iconUpdate() = runGenericTest {
+ if (
+ (testCase.isFaceOnly || testCase.isCoex) &&
+ testCase.authenticatedByFace &&
+ testCase.confirmationRequested
+ ) {
+ val iconAsset by collectLastValue(iconViewModel.iconAsset)
+ val iconContentDescriptionId by collectLastValue(iconViewModel.contentDescriptionId)
+ val shouldAnimateIconView by collectLastValue(iconViewModel.shouldAnimateIconView)
+
+ viewModel.showAuthenticated(
+ modality = testCase.authenticatedModality,
+ dismissAfterDelay = DELAY
+ )
+
+ viewModel.confirmAuthenticated()
+
+ if (testCase.isFaceOnly) {
+ val shouldRepeatAnimation by collectLastValue(iconViewModel.shouldRepeatAnimation)
+ val shouldPulseAnimation by collectLastValue(iconViewModel.shouldPulseAnimation)
+
+ assertThat(shouldPulseAnimation!!).isEqualTo(false)
+ assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_dark_to_checkmark)
+ assertThat(iconContentDescriptionId)
+ .isEqualTo(R.string.biometric_dialog_face_icon_description_confirmed)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldRepeatAnimation).isEqualTo(false)
+ }
+
+ // explicit flow because confirmation requested
+ if (testCase.isCoex) {
+ val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
+ val shouldAnimateIconOverlay by
+ collectLastValue(iconViewModel.shouldAnimateIconOverlay)
+
+ // TODO: Update when SFPS co-ex is implemented
+ if (testCase.sensorType != FingerprintSensorProperties.TYPE_POWER_BUTTON) {
+ assertThat(iconAsset)
+ .isEqualTo(R.raw.fingerprint_dialogue_unlocked_to_checkmark_success_lottie)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
+ assertThat(iconContentDescriptionId)
+ .isEqualTo(R.string.fingerprint_dialog_touch_sensor)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
+ }
+ }
+ }
+ }
+
+ @Test
+ fun sfpsIconUpdates_onConfigurationChanged() = runGenericTest {
+ if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
+ val testConfig = Configuration()
+ val folded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP - 1
+ val unfolded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP + 1
+ val currentIcon by collectLastValue(iconViewModel.iconAsset)
+
+ testConfig.smallestScreenWidthDp = folded
+ iconViewModel.onConfigurationChanged(testConfig)
+ val foldedIcon = currentIcon
+
+ testConfig.smallestScreenWidthDp = unfolded
+ iconViewModel.onConfigurationChanged(testConfig)
+ val unfoldedIcon = currentIcon
+
+ assertThat(foldedIcon).isNotEqualTo(unfoldedIcon)
+ }
+ }
+
+ @Test
+ fun sfpsIconUpdates_onRotation() = runGenericTest {
+ if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
+ val currentIcon by collectLastValue(iconViewModel.iconAsset)
+
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
+ val iconRotation0 = currentIcon
+
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+ val iconRotation90 = currentIcon
+
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+ val iconRotation180 = currentIcon
+
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+ val iconRotation270 = currentIcon
+
+ assertThat(iconRotation0).isEqualTo(iconRotation180)
+ assertThat(iconRotation0).isNotEqualTo(iconRotation90)
+ assertThat(iconRotation0).isNotEqualTo(iconRotation270)
+ }
+ }
+
+ @Test
+ fun sfpsIconUpdates_onRearDisplayMode() = runGenericTest {
+ if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
+ val currentIcon by collectLastValue(iconViewModel.iconAsset)
+
+ displayStateRepository.setIsInRearDisplayMode(false)
+ val iconNotRearDisplayMode = currentIcon
+
+ displayStateRepository.setIsInRearDisplayMode(true)
+ val iconRearDisplayMode = currentIcon
+
+ assertThat(iconNotRearDisplayMode).isNotEqualTo(iconRearDisplayMode)
+ }
+ }
+
private suspend fun TestScope.showAuthenticated(
authenticatedModality: BiometricModality,
expectConfirmation: Boolean,
@@ -213,7 +694,6 @@
val authenticated by collectLastValue(viewModel.isAuthenticated)
val fpStartMode by collectLastValue(viewModel.fingerprintStartMode)
val size by collectLastValue(viewModel.size)
- val legacyState by collectLastValue(viewModel.legacyState)
val authWithSmallPrompt =
testCase.shouldStartAsImplicitFlow &&
@@ -221,14 +701,12 @@
assertThat(authenticating).isTrue()
assertThat(authenticated?.isNotAuthenticated).isTrue()
assertThat(size).isEqualTo(if (authWithSmallPrompt) PromptSize.SMALL else PromptSize.MEDIUM)
- assertThat(legacyState).isEqualTo(BiometricState.STATE_AUTHENTICATING)
assertButtonsVisible(negative = !authWithSmallPrompt)
- val delay = 1000L
- viewModel.showAuthenticated(authenticatedModality, delay)
+ viewModel.showAuthenticated(authenticatedModality, DELAY)
assertThat(authenticated?.isAuthenticated).isTrue()
- assertThat(authenticated?.delay).isEqualTo(delay)
+ assertThat(authenticated?.delay).isEqualTo(DELAY)
assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation)
assertThat(size)
.isEqualTo(
@@ -238,14 +716,7 @@
PromptSize.SMALL
}
)
- assertThat(legacyState)
- .isEqualTo(
- if (expectConfirmation) {
- BiometricState.STATE_PENDING_CONFIRMATION
- } else {
- BiometricState.STATE_AUTHENTICATED
- }
- )
+
assertButtonsVisible(
cancel = expectConfirmation,
confirm = expectConfirmation,
@@ -298,7 +769,6 @@
val message by collectLastValue(viewModel.message)
val messageVisible by collectLastValue(viewModel.isIndicatorMessageVisible)
val size by collectLastValue(viewModel.size)
- val legacyState by collectLastValue(viewModel.legacyState)
val canTryAgainNow by collectLastValue(viewModel.canTryAgainNow)
val errorJob = launch {
@@ -312,7 +782,6 @@
assertThat(size).isEqualTo(PromptSize.MEDIUM)
assertThat(message).isEqualTo(PromptMessage.Error(errorMessage))
assertThat(messageVisible).isTrue()
- assertThat(legacyState).isEqualTo(BiometricState.STATE_ERROR)
// temporary error should disappear after a delay
errorJob.join()
@@ -323,17 +792,6 @@
assertThat(message).isEqualTo(PromptMessage.Empty)
assertThat(messageVisible).isFalse()
}
- val clearIconError = !restart
- assertThat(legacyState)
- .isEqualTo(
- if (restart) {
- BiometricState.STATE_AUTHENTICATING
- } else if (clearIconError) {
- BiometricState.STATE_IDLE
- } else {
- BiometricState.STATE_HELP
- }
- )
assertThat(authenticating).isEqualTo(restart)
assertThat(authenticated?.isNotAuthenticated).isTrue()
@@ -488,7 +946,6 @@
val authenticated by collectLastValue(viewModel.isAuthenticated)
val message by collectLastValue(viewModel.message)
val size by collectLastValue(viewModel.size)
- val legacyState by collectLastValue(viewModel.legacyState)
val canTryAgain by collectLastValue(viewModel.canTryAgainNow)
assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation)
@@ -506,7 +963,6 @@
assertThat(authenticating).isFalse()
assertThat(authenticated?.isAuthenticated).isTrue()
- assertThat(legacyState).isEqualTo(BiometricState.STATE_AUTHENTICATED)
assertThat(canTryAgain).isFalse()
}
@@ -524,7 +980,6 @@
val authenticated by collectLastValue(viewModel.isAuthenticated)
val message by collectLastValue(viewModel.message)
val size by collectLastValue(viewModel.size)
- val legacyState by collectLastValue(viewModel.legacyState)
val canTryAgain by collectLastValue(viewModel.canTryAgainNow)
assertThat(authenticating).isFalse()
@@ -532,8 +987,6 @@
assertThat(authenticated?.isAuthenticated).isTrue()
if (testCase.isFaceOnly && expectConfirmation) {
- assertThat(legacyState).isEqualTo(BiometricState.STATE_PENDING_CONFIRMATION)
-
assertThat(size).isEqualTo(PromptSize.MEDIUM)
assertButtonsVisible(
cancel = true,
@@ -543,8 +996,6 @@
viewModel.confirmAuthenticated()
assertThat(message).isEqualTo(PromptMessage.Empty)
assertButtonsVisible()
- } else {
- assertThat(legacyState).isEqualTo(BiometricState.STATE_AUTHENTICATED)
}
}
@@ -563,7 +1014,6 @@
val authenticated by collectLastValue(viewModel.isAuthenticated)
val message by collectLastValue(viewModel.message)
val size by collectLastValue(viewModel.size)
- val legacyState by collectLastValue(viewModel.legacyState)
val canTryAgain by collectLastValue(viewModel.canTryAgainNow)
assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation)
@@ -581,7 +1031,6 @@
assertThat(authenticating).isFalse()
assertThat(authenticated?.isAuthenticated).isTrue()
- assertThat(legacyState).isEqualTo(BiometricState.STATE_AUTHENTICATED)
assertThat(canTryAgain).isFalse()
}
@@ -610,12 +1059,10 @@
val message by collectLastValue(viewModel.message)
val messageVisible by collectLastValue(viewModel.isIndicatorMessageVisible)
val size by collectLastValue(viewModel.size)
- val legacyState by collectLastValue(viewModel.legacyState)
viewModel.showHelp(helpMessage)
assertThat(size).isEqualTo(PromptSize.MEDIUM)
- assertThat(legacyState).isEqualTo(BiometricState.STATE_HELP)
assertThat(message).isEqualTo(PromptMessage.Help(helpMessage))
assertThat(messageVisible).isTrue()
@@ -632,7 +1079,6 @@
val message by collectLastValue(viewModel.message)
val messageVisible by collectLastValue(viewModel.isIndicatorMessageVisible)
val size by collectLastValue(viewModel.size)
- val legacyState by collectLastValue(viewModel.legacyState)
val confirmationRequired by collectLastValue(viewModel.isConfirmationRequired)
if (testCase.isCoex && testCase.authenticatedByFingerprint) {
@@ -642,11 +1088,7 @@
viewModel.showHelp(helpMessage)
assertThat(size).isEqualTo(PromptSize.MEDIUM)
- if (confirmationRequired == true) {
- assertThat(legacyState).isEqualTo(BiometricState.STATE_PENDING_CONFIRMATION)
- } else {
- assertThat(legacyState).isEqualTo(BiometricState.STATE_AUTHENTICATED)
- }
+
assertThat(message).isEqualTo(PromptMessage.Help(helpMessage))
assertThat(messageVisible).isTrue()
assertThat(authenticating).isFalse()
@@ -785,6 +1227,15 @@
authenticatedModality = BiometricModality.Fingerprint,
),
TestCase(
+ fingerprint =
+ fingerprintSensorPropertiesInternal(
+ strong = true,
+ sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+ )
+ .first(),
+ authenticatedModality = BiometricModality.Fingerprint,
+ ),
+ TestCase(
face = faceSensorPropertiesInternal(strong = true).first(),
authenticatedModality = BiometricModality.Face,
confirmationRequested = true,
@@ -794,6 +1245,16 @@
authenticatedModality = BiometricModality.Fingerprint,
confirmationRequested = true,
),
+ TestCase(
+ fingerprint =
+ fingerprintSensorPropertiesInternal(
+ strong = true,
+ sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+ )
+ .first(),
+ authenticatedModality = BiometricModality.Fingerprint,
+ confirmationRequested = true,
+ ),
)
private val coexTestCases =
@@ -834,7 +1295,9 @@
val modality =
when {
fingerprint != null && face != null -> "coex"
- fingerprint != null -> "fingerprint only"
+ fingerprint != null && fingerprint.isAnySidefpsType -> "fingerprint only, sideFps"
+ fingerprint != null && !fingerprint.isAnySidefpsType ->
+ "fingerprint only, non-sideFps"
face != null -> "face only"
else -> "?"
}
@@ -864,6 +1327,8 @@
val isCoex: Boolean
get() = face != null && fingerprint != null
+ @FingerprintSensorProperties.SensorType val sensorType: Int? = fingerprint?.sensorType
+
val shouldStartAsImplicitFlow: Boolean
get() = (isFaceOnly || isCoex) && !confirmationRequested
}
@@ -890,3 +1355,5 @@
BiometricModalities(fingerprintProperties = fingerprint, faceProperties = face),
)
}
+
+internal const val INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP = 600
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
index cc4eca5..b48bc1d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
@@ -48,6 +48,7 @@
import com.android.systemui.res.R.string.kg_trust_agent_disabled
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.KotlinArgumentCaptor
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -86,6 +87,7 @@
@Mock private lateinit var countDownTimerUtil: CountDownTimerUtil
@Mock private lateinit var systemPropertiesHelper: SystemPropertiesHelper
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
private lateinit var testScope: TestScope
@@ -119,6 +121,7 @@
keyguardUpdateMonitor,
fakeTrustRepository,
testScope.backgroundScope,
+ mSelectedUserInteractor,
)
underTest =
BouncerMessageInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
index 9a5b4585..f6b284f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
@@ -25,7 +25,6 @@
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.DejankUtils
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN
@@ -37,7 +36,9 @@
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.android.systemui.utils.os.FakeHandler
@@ -70,6 +71,7 @@
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
private lateinit var mainHandler: FakeHandler
private lateinit var underTest: PrimaryBouncerInteractor
private lateinit var resources: TestableResources
@@ -100,6 +102,7 @@
keyguardUpdateMonitor,
trustRepository,
testScope.backgroundScope,
+ mSelectedUserInteractor,
)
whenever(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
whenever(repository.primaryBouncerShow.value).thenReturn(false)
@@ -108,6 +111,27 @@
}
@Test
+ fun show_nullDelegate() {
+ testScope.run {
+ whenever(bouncerView.delegate).thenReturn(null)
+ mainHandler.setMode(FakeHandler.Mode.QUEUEING)
+
+ // WHEN bouncer show is requested
+ underTest.show(true)
+
+ // WHEN all queued messages are dispatched
+ mainHandler.dispatchQueuedMessages()
+
+ // THEN primary bouncer state doesn't update to show since delegate was null
+ verify(repository, never()).setPrimaryShow(true)
+ verify(repository, never()).setPrimaryShowingSoon(false)
+ verify(mPrimaryBouncerCallbackInteractor, never()).dispatchStartingToShow()
+ verify(mPrimaryBouncerCallbackInteractor, never())
+ .dispatchVisibilityChanged(View.VISIBLE)
+ }
+ }
+
+ @Test
fun testShow_isScrimmed() {
underTest.show(true)
verify(repository).setKeyguardAuthenticatedBiometrics(null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
index 2018e61..d1b120e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
@@ -28,8 +28,8 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.TrustRepository
-import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.utils.os.FakeHandler
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.TestScope
@@ -51,8 +51,8 @@
@Mock private lateinit var primaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
- @Mock private lateinit var keyguardBypassController: KeyguardBypassController
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
private val mainHandler = FakeHandler(Looper.getMainLooper())
private lateinit var underTest: PrimaryBouncerInteractor
@@ -74,6 +74,7 @@
keyguardUpdateMonitor,
Mockito.mock(TrustRepository::class.java),
TestScope().backgroundScope,
+ mSelectedUserInteractor,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
index 802f8e6..b75f3e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
@@ -32,8 +32,8 @@
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.TrustRepository
-import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.utils.os.FakeHandler
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
@@ -60,7 +60,7 @@
@Mock private lateinit var primaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
- @Mock private lateinit var keyguardBypassController: KeyguardBypassController
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
private val mainHandler = FakeHandler(Looper.getMainLooper())
val repository = FakeKeyguardBouncerRepository()
@@ -82,6 +82,7 @@
keyguardUpdateMonitor,
Mockito.mock(TrustRepository::class.java),
TestScope().backgroundScope,
+ mSelectedUserInteractor,
)
underTest = KeyguardBouncerViewModel(bouncerView, bouncerInteractor)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
index 037c1ba..6d3cc4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
@@ -28,11 +28,11 @@
import com.android.systemui.ActivityIntentHelper
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.KotlinArgumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
@@ -46,8 +46,8 @@
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(JUnit4::class)
@@ -74,7 +74,7 @@
@Mock
lateinit var contentResolver: ContentResolver
@Mock
- lateinit var userTracker: UserTracker
+ lateinit var mSelectedUserInteractor: SelectedUserInteractor
private lateinit var underTest: CameraGestureHelper
@@ -103,7 +103,7 @@
cameraIntents = cameraIntents,
contentResolver = contentResolver,
uiExecutor = MoreExecutors.directExecutor(),
- userTracker = userTracker,
+ selectedUserInteractor = mSelectedUserInteractor,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
index 442bf91..80fe9e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
@@ -40,6 +40,7 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.sensors.ThresholdSensor;
@@ -75,6 +76,8 @@
private ShadeExpansionStateManager mShadeExpansionStateManager;
@Mock
private BatteryController mBatteryController;
+ @Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
private final DockManagerFake mDockManager = new DockManagerFake();
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
@@ -90,7 +93,7 @@
mKeyguardUpdateMonitor, mHistoryTracker, mProximitySensor,
mStatusBarStateController, mKeyguardStateController, mShadeExpansionStateManager,
mBatteryController,
- mDockManager, mFakeExecutor, mFakeSystemClock);
+ mDockManager, mFakeExecutor, mFakeSystemClock, () -> mSelectedUserInteractor);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
index 15e5e1c..80b281e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
@@ -37,6 +37,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import org.junit.Before;
import org.junit.Test;
@@ -63,6 +64,8 @@
private WallpaperManager mWallpaperManager;
@Mock
private DumpManager mDumpManager;
+ @Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
private ColorExtractor.GradientColors mColors;
private SysuiColorExtractor mColorExtractor;
@@ -83,7 +86,8 @@
mock(ConfigurationController.class),
mWallpaperManager,
mDumpManager,
- true /* immediately */);
+ true /* immediately */,
+ () -> mSelectedUserInteractor);
}
@Test
@@ -125,7 +129,8 @@
configurationController,
mWallpaperManager,
mDumpManager,
- true /* immediately */);
+ true /* immediately */,
+ () -> mSelectedUserInteractor);
verify(configurationController).addCallback(eq(sysuiColorExtractor));
reset(tonal);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index 91409a3..fcb191b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -12,9 +12,10 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.communal.data.model.CommunalWidgetMetadata
-import com.android.systemui.communal.shared.CommunalContentSize
+import com.android.systemui.communal.shared.model.CommunalContentSize
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.FakeLogBuffer
@@ -38,6 +39,7 @@
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@@ -58,10 +60,16 @@
@Mock private lateinit var userTracker: UserTracker
- @Mock private lateinit var featureFlags: FeatureFlags
+ @Mock private lateinit var featureFlags: FeatureFlagsClassic
@Mock private lateinit var stopwatchProviderInfo: AppWidgetProviderInfo
+ @Mock private lateinit var providerInfoA: AppWidgetProviderInfo
+
+ @Mock private lateinit var providerInfoB: AppWidgetProviderInfo
+
+ @Mock private lateinit var providerInfoC: AppWidgetProviderInfo
+
private lateinit var communalRepository: FakeCommunalRepository
private lateinit var logBuffer: LogBuffer
@@ -70,29 +78,34 @@
private val testScope = TestScope(testDispatcher)
+ private val fakeAllowlist =
+ listOf(
+ "com.android.fake/WidgetProviderA",
+ "com.android.fake/WidgetProviderB",
+ "com.android.fake/WidgetProviderC",
+ )
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
logBuffer = FakeLogBuffer.Factory.create()
-
- featureFlagEnabled(true)
communalRepository = FakeCommunalRepository()
- communalRepository.setIsCommunalEnabled(true)
- overrideResource(
- R.array.config_communalWidgetAllowlist,
- arrayOf(componentName1, componentName2)
- )
+ communalEnabled(true)
+ widgetOnKeyguardEnabled(true)
+ setAppWidgetIds(emptyList())
+
+ overrideResource(R.array.config_communalWidgetAllowlist, fakeAllowlist.toTypedArray())
whenever(stopwatchProviderInfo.loadLabel(any())).thenReturn("Stopwatch")
whenever(userTracker.userHandle).thenReturn(userHandle)
}
@Test
- fun broadcastReceiver_featureDisabled_doNotRegisterUserUnlockedBroadcastReceiver() =
+ fun broadcastReceiver_communalDisabled_doNotRegisterUserUnlockedBroadcastReceiver() =
testScope.runTest {
- featureFlagEnabled(false)
+ communalEnabled(false)
val repository = initCommunalWidgetRepository()
collectLastValue(repository.stopwatchAppWidgetInfo)()
verifyBroadcastReceiverNeverRegistered()
@@ -129,7 +142,7 @@
job.cancel()
runCurrent()
- Mockito.verify(broadcastDispatcher).unregisterReceiver(receiver)
+ verify(broadcastDispatcher).unregisterReceiver(receiver)
}
@Test
@@ -166,7 +179,7 @@
installedProviders(listOf(stopwatchProviderInfo))
val repository = initCommunalWidgetRepository()
collectLastValue(repository.stopwatchAppWidgetInfo)()
- Mockito.verify(appWidgetHost).allocateAppWidgetId()
+ verify(appWidgetHost).allocateAppWidgetId()
}
@Test
@@ -185,8 +198,8 @@
// Verify app widget id allocated
assertThat(lastStopwatchProviderInfo()?.appWidgetId).isEqualTo(123456)
- Mockito.verify(appWidgetHost).allocateAppWidgetId()
- Mockito.verify(appWidgetHost, Mockito.never()).deleteAppWidgetId(anyInt())
+ verify(appWidgetHost).allocateAppWidgetId()
+ verify(appWidgetHost, Mockito.never()).deleteAppWidgetId(anyInt())
// User locked again
userUnlocked(false)
@@ -194,7 +207,7 @@
// Verify app widget id deleted
assertThat(lastStopwatchProviderInfo()).isNull()
- Mockito.verify(appWidgetHost).deleteAppWidgetId(123456)
+ verify(appWidgetHost).deleteAppWidgetId(123456)
}
@Test
@@ -203,13 +216,13 @@
userUnlocked(false)
val repository = initCommunalWidgetRepository()
collectLastValue(repository.stopwatchAppWidgetInfo)()
- Mockito.verify(appWidgetHost, Mockito.never()).startListening()
+ verify(appWidgetHost, Mockito.never()).startListening()
userUnlocked(true)
broadcastReceiverUpdate()
collectLastValue(repository.stopwatchAppWidgetInfo)()
- Mockito.verify(appWidgetHost).startListening()
+ verify(appWidgetHost).startListening()
}
@Test
@@ -223,14 +236,14 @@
broadcastReceiverUpdate()
collectLastValue(repository.stopwatchAppWidgetInfo)()
- Mockito.verify(appWidgetHost).startListening()
- Mockito.verify(appWidgetHost, Mockito.never()).stopListening()
+ verify(appWidgetHost).startListening()
+ verify(appWidgetHost, Mockito.never()).stopListening()
userUnlocked(false)
broadcastReceiverUpdate()
collectLastValue(repository.stopwatchAppWidgetInfo)()
- Mockito.verify(appWidgetHost).stopListening()
+ verify(appWidgetHost).stopListening()
}
@Test
@@ -241,21 +254,80 @@
assertThat(
listOf(
CommunalWidgetMetadata(
- componentName = componentName1,
- priority = 2,
- sizes = listOf(CommunalContentSize.HALF)
+ componentName = fakeAllowlist[0],
+ priority = 3,
+ sizes = listOf(CommunalContentSize.HALF),
),
CommunalWidgetMetadata(
- componentName = componentName2,
+ componentName = fakeAllowlist[1],
+ priority = 2,
+ sizes = listOf(CommunalContentSize.HALF),
+ ),
+ CommunalWidgetMetadata(
+ componentName = fakeAllowlist[2],
priority = 1,
- sizes = listOf(CommunalContentSize.HALF)
- )
+ sizes = listOf(CommunalContentSize.HALF),
+ ),
)
)
.containsExactly(*communalWidgetAllowlist.toTypedArray())
}
}
+ // This behavior is temporary before the local database is set up.
+ @Test
+ fun communalWidgets_withPreviouslyBoundWidgets_removeEachBinding() =
+ testScope.runTest {
+ whenever(appWidgetHost.allocateAppWidgetId()).thenReturn(1, 2, 3)
+ setAppWidgetIds(listOf(1, 2, 3))
+ whenever(appWidgetManager.getAppWidgetInfo(anyInt())).thenReturn(providerInfoA)
+ userUnlocked(true)
+
+ val repository = initCommunalWidgetRepository()
+
+ collectLastValue(repository.communalWidgets)()
+
+ verify(appWidgetHost).deleteAppWidgetId(1)
+ verify(appWidgetHost).deleteAppWidgetId(2)
+ verify(appWidgetHost).deleteAppWidgetId(3)
+ }
+
+ @Test
+ fun communalWidgets_allowlistNotEmpty_bindEachWidgetFromTheAllowlist() =
+ testScope.runTest {
+ whenever(appWidgetHost.allocateAppWidgetId()).thenReturn(0, 1, 2)
+ userUnlocked(true)
+
+ whenever(appWidgetManager.getAppWidgetInfo(0)).thenReturn(providerInfoA)
+ whenever(appWidgetManager.getAppWidgetInfo(1)).thenReturn(providerInfoB)
+ whenever(appWidgetManager.getAppWidgetInfo(2)).thenReturn(providerInfoC)
+
+ val repository = initCommunalWidgetRepository()
+
+ val inventory by collectLastValue(repository.communalWidgets)
+
+ assertThat(
+ listOf(
+ CommunalWidgetContentModel(
+ appWidgetId = 0,
+ providerInfo = providerInfoA,
+ priority = 3,
+ ),
+ CommunalWidgetContentModel(
+ appWidgetId = 1,
+ providerInfo = providerInfoB,
+ priority = 2,
+ ),
+ CommunalWidgetContentModel(
+ appWidgetId = 2,
+ providerInfo = providerInfoC,
+ priority = 1,
+ ),
+ )
+ )
+ .containsExactly(*inventory!!.toTypedArray())
+ }
+
private fun initCommunalWidgetRepository(): CommunalWidgetRepositoryImpl {
return CommunalWidgetRepositoryImpl(
context,
@@ -272,7 +344,7 @@
}
private fun verifyBroadcastReceiverRegistered() {
- Mockito.verify(broadcastDispatcher)
+ verify(broadcastDispatcher)
.registerReceiver(
any(),
any(),
@@ -284,7 +356,7 @@
}
private fun verifyBroadcastReceiverNeverRegistered() {
- Mockito.verify(broadcastDispatcher, Mockito.never())
+ verify(broadcastDispatcher, Mockito.never())
.registerReceiver(
any(),
any(),
@@ -297,7 +369,7 @@
private fun broadcastReceiverUpdate(): BroadcastReceiver {
val broadcastReceiverCaptor = kotlinArgumentCaptor<BroadcastReceiver>()
- Mockito.verify(broadcastDispatcher)
+ verify(broadcastDispatcher)
.registerReceiver(
broadcastReceiverCaptor.capture(),
any(),
@@ -310,7 +382,11 @@
return broadcastReceiverCaptor.value
}
- private fun featureFlagEnabled(enabled: Boolean) {
+ private fun communalEnabled(enabled: Boolean) {
+ communalRepository.setIsCommunalEnabled(enabled)
+ }
+
+ private fun widgetOnKeyguardEnabled(enabled: Boolean) {
whenever(featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)).thenReturn(enabled)
}
@@ -322,8 +398,7 @@
whenever(appWidgetManager.installedProviders).thenReturn(providers)
}
- companion object {
- const val componentName1 = "component name 1"
- const val componentName2 = "component name 2"
+ private fun setAppWidgetIds(ids: List<Int>) {
+ whenever(appWidgetHost.appWidgetIds).thenReturn(ids.toIntArray())
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index cdc42e0..8e21f29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -22,7 +22,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.FakeCommunalRepository
import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
-import com.android.systemui.communal.shared.CommunalAppWidgetInfo
+import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
import com.android.systemui.coroutines.collectLastValue
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
index 7e1edd2..ba578a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -51,6 +51,7 @@
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.UdfpsController;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.wakelock.WakeLockFake;
import com.android.systemui.utils.os.FakeHandler;
@@ -85,6 +86,8 @@
private DozeLog mDozeLog;
@Mock
private DozeScreenBrightness mDozeScreenBrightness;
+ @Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
@Before
public void setUp() throws Exception {
@@ -100,7 +103,7 @@
mWakeLock = new WakeLockFake();
mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeHost, mDozeParameters,
mWakeLock, mAuthController, mUdfpsControllerProvider, mDozeLog,
- mDozeScreenBrightness);
+ mDozeScreenBrightness, mSelectedUserInteractor);
}
@Test
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 a88a8e5..3cc0451 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -52,9 +52,9 @@
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.doze.DozeSensors.TriggerSensor;
import com.android.systemui.plugins.SensorManagerPlugin;
-import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.settings.FakeSettings;
@@ -102,7 +102,7 @@
@Mock
private DevicePostureController mDevicePostureController;
@Mock
- private UserTracker mUserTracker;
+ private SelectedUserInteractor mSelectedUserInteractor;
@Mock
private ProximitySensor mProximitySensor;
@@ -122,7 +122,8 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mTestableLooper = TestableLooper.get(this);
- when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
+ when(mSelectedUserInteractor.getSelectedUserId())
+ .thenReturn(ActivityManager.getCurrentUser());
when(mAmbientDisplayConfiguration.tapSensorTypeMapping())
.thenReturn(new String[]{"tapSensor"});
when(mAmbientDisplayConfiguration.getWakeLockScreenDebounce()).thenReturn(5000L);
@@ -435,7 +436,7 @@
DozeSensors dozeSensors = new DozeSensors(mResources, mSensorManager, mDozeParameters,
mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
mProximitySensor, mFakeSettings, mAuthController,
- mDevicePostureController, mUserTracker);
+ mDevicePostureController, mSelectedUserInteractor);
for (TriggerSensor sensor : dozeSensors.mTriggerSensors) {
assertFalse(sensor.mIgnoresSetting);
@@ -480,7 +481,7 @@
DozeSensors dozeSensors = new DozeSensors(mResources, mSensorManager, mDozeParameters,
mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
mProximitySensor, mFakeSettings, mAuthController,
- mDevicePostureController, mUserTracker);
+ mDevicePostureController, mSelectedUserInteractor);
for (TriggerSensor sensor : dozeSensors.mTriggerSensors) {
// THEN lift to wake's TriggerSensor enabledBySettings is false
@@ -499,7 +500,7 @@
DozeSensors dozeSensors = new DozeSensors(mResources, mSensorManager, mDozeParameters,
mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
mProximitySensor, mFakeSettings, mAuthController,
- mDevicePostureController, mUserTracker);
+ mDevicePostureController, mSelectedUserInteractor);
for (TriggerSensor sensor : dozeSensors.mTriggerSensors) {
// THEN lift to wake's TriggerSensor enabledBySettings is true
@@ -514,7 +515,7 @@
super(mResources, mSensorManager, mDozeParameters,
mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
mProximitySensor, mFakeSettings, mAuthController,
- mDevicePostureController, mUserTracker);
+ mDevicePostureController, mSelectedUserInteractor);
for (TriggerSensor sensor : mTriggerSensors) {
if (sensor instanceof PluginSensor
&& ((PluginSensor) sensor).mPluginSensor.getType()
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 494e230..3a6b075 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -53,6 +53,7 @@
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.FakeThreadFactory;
import com.android.systemui.util.sensors.AsyncSensorManager;
@@ -101,6 +102,8 @@
@Mock
private UserTracker mUserTracker;
@Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
+ @Mock
private SessionTracker mSessionTracker;
private DozeTriggers mTriggers;
@@ -134,7 +137,7 @@
asyncSensorManager, wakeLock, mDockManager, mProximitySensor,
mProximityCheck, mDozeLog, mBroadcastDispatcher, new FakeSettings(),
mAuthController, mUiEventLogger, mSessionTracker, mKeyguardStateController,
- mDevicePostureController, mUserTracker);
+ mDevicePostureController, mUserTracker, mSelectedUserInteractor);
mTriggers.setDozeMachine(mMachine);
waitForSensorManager();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 2d3f69d..00009f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -76,6 +76,7 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.telephony.TelephonyListenerManager;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.RingerModeLiveData;
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.settings.FakeGlobalSettings;
@@ -134,6 +135,7 @@
@Mock private ShadeController mShadeController;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private DialogLaunchAnimator mDialogLaunchAnimator;
+ @Mock private SelectedUserInteractor mSelectedUserInteractor;
@Mock private OnBackInvokedDispatcher mOnBackInvokedDispatcher;
@Captor private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
@@ -186,12 +188,14 @@
mPackageManager,
mShadeController,
mKeyguardUpdateMonitor,
- mDialogLaunchAnimator);
+ mDialogLaunchAnimator,
+ mSelectedUserInteractor);
mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
ColorExtractor.GradientColors backdropColors = new ColorExtractor.GradientColors();
backdropColors.setMainColor(Color.BLACK);
when(mColorExtractor.getNeutralColors()).thenReturn(backdropColors);
+ when(mSelectedUserInteractor.getSelectedUserId()).thenReturn(0);
}
@Test
@@ -568,7 +572,8 @@
@Test
public void testOnLockScreen_disableSmartLock() {
mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int expectedUser = 100;
+ doReturn(expectedUser).when(mSelectedUserInteractor).getSelectedUserId();
doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
@@ -586,7 +591,7 @@
mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */);
// Then smart lock will be disabled
- verify(mLockPatternUtils).requireCredentialEntry(eq(user));
+ verify(mLockPatternUtils).requireCredentialEntry(eq(expectedUser));
// hide dialog again
mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index a646823..2b280c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -117,6 +117,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -187,6 +188,7 @@
private @Mock ShadeExpansionStateManager mShadeExpansionStateManager;
private @Mock ShadeInteractor mShadeInteractor;
private @Mock ShadeWindowLogger mShadeWindowLogger;
+ private @Mock SelectedUserInteractor mSelectedUserInteractor;
private @Captor ArgumentCaptor<KeyguardStateController.Callback>
mKeyguardStateControllerCallback;
private @Captor ArgumentCaptor<KeyguardUpdateMonitorCallback>
@@ -213,7 +215,7 @@
private @Mock SystemPropertiesHelper mSystemPropertiesHelper;
private FakeFeatureFlags mFeatureFlags;
- private int mInitialUserId;
+ private final int mDefaultUserId = 100;
@Before
public void setUp() throws Exception {
@@ -233,6 +235,8 @@
.thenReturn(mock(Flow.class));
when(mDreamingToLockscreenTransitionViewModel.getTransitionEnded())
.thenReturn(mock(Flow.class));
+ when(mSelectedUserInteractor.getSelectedUserId()).thenReturn(mDefaultUserId);
+ when(mSelectedUserInteractor.getSelectedUserId(anyBoolean())).thenReturn(mDefaultUserId);
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(
mContext,
new FakeWindowRootViewComponent.Factory(mock(WindowRootView.class)),
@@ -251,9 +255,11 @@
mAuthController,
mShadeExpansionStateManager,
() -> mShadeInteractor,
- mShadeWindowLogger);
+ mShadeWindowLogger,
+ () -> mSelectedUserInteractor);
mFeatureFlags = new FakeFeatureFlags();
mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false);
+ mFeatureFlags.set(Flags.REFACTOR_GETCURRENTUSER, true);
DejankUtils.setImmediate(true);
@@ -266,12 +272,6 @@
}).when(mKeyguardStateController).notifyKeyguardGoingAway(anyBoolean());
createAndStartViewMediator();
- mInitialUserId = KeyguardUpdateMonitor.getCurrentUser();
- }
-
- @After
- public void teardown() {
- KeyguardUpdateMonitor.setCurrentUser(mInitialUserId);
}
/**
@@ -451,7 +451,7 @@
mViewMediator.setKeyguardEnabled(false);
TestableLooper.get(this).processAllMessages();
- mViewMediator.mViewMediatorCallback.keyguardDonePending(mUpdateMonitor.getCurrentUser());
+ mViewMediator.mViewMediatorCallback.keyguardDonePending(mDefaultUserId);
mViewMediator.mViewMediatorCallback.readyForKeyguardDone();
final ArgumentCaptor<Runnable> animationRunnableCaptor =
ArgumentCaptor.forClass(Runnable.class);
@@ -617,8 +617,8 @@
public void lockAfterScreenTimeoutUsesValueFromSettings() {
int currentUserId = 99;
int userSpecificTimeout = 5999;
- KeyguardUpdateMonitor.setCurrentUser(currentUserId);
+ when(mSelectedUserInteractor.getSelectedUserId()).thenReturn(currentUserId);
when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false);
when(mDevicePolicyManager.getMaximumTimeToLock(null, currentUserId)).thenReturn(0L);
when(mSecureSettings.getIntForUser(LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
@@ -718,7 +718,7 @@
startMockKeyguardExitAnimation();
assertTrue(mViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehind());
- mViewMediator.mViewMediatorCallback.keyguardDonePending(mUpdateMonitor.getCurrentUser());
+ mViewMediator.mViewMediatorCallback.keyguardDonePending(mDefaultUserId);
mViewMediator.mViewMediatorCallback.readyForKeyguardDone();
TestableLooper.get(this).processAllMessages();
verify(mKeyguardUnlockAnimationController).notifyFinishedKeyguardExitAnimation(false);
@@ -782,7 +782,7 @@
// Verify keyguard told of authentication
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean());
- mViewMediator.mViewMediatorCallback.keyguardDonePending(mUpdateMonitor.getCurrentUser());
+ mViewMediator.mViewMediatorCallback.keyguardDonePending(mDefaultUserId);
mViewMediator.mViewMediatorCallback.readyForKeyguardDone();
final ArgumentCaptor<Runnable> animationRunnableCaptor =
ArgumentCaptor.forClass(Runnable.class);
@@ -814,7 +814,7 @@
// Verify keyguard told of authentication
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean());
clearInvocations(mStatusBarKeyguardViewManager);
- mViewMediator.mViewMediatorCallback.keyguardDonePending(mUpdateMonitor.getCurrentUser());
+ mViewMediator.mViewMediatorCallback.keyguardDonePending(mDefaultUserId);
mViewMediator.mViewMediatorCallback.readyForKeyguardDone();
final ArgumentCaptor<Runnable> animationRunnableCaptor =
ArgumentCaptor.forClass(Runnable.class);
@@ -844,7 +844,7 @@
// Verify keyguard told of authentication
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean());
- mViewMediator.mViewMediatorCallback.keyguardDonePending(mUpdateMonitor.getCurrentUser());
+ mViewMediator.mViewMediatorCallback.keyguardDonePending(mDefaultUserId);
mViewMediator.mViewMediatorCallback.readyForKeyguardDone();
final ArgumentCaptor<Runnable> animationRunnableCaptor =
ArgumentCaptor.forClass(Runnable.class);
@@ -1111,7 +1111,8 @@
mDispatcher,
() -> mDreamingToLockscreenTransitionViewModel,
mSystemPropertiesHelper,
- () -> mock(WindowManagerLockscreenVisibilityManager.class));
+ () -> mock(WindowManagerLockscreenVisibilityManager.class),
+ mSelectedUserInteractor);
mViewMediator.start();
mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null, null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index b9119e1..ef03fdf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -30,14 +30,10 @@
import com.android.systemui.doze.DozeTransitionCallback
import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.dreams.DreamOverlayCallbackController
-import com.android.systemui.keyguard.ScreenLifecycle
-import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.phone.BiometricUnlockController
-import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -47,7 +43,6 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
@@ -68,13 +63,10 @@
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var keyguardStateController: KeyguardStateController
- @Mock private lateinit var screenLifecycle: ScreenLifecycle
- @Mock private lateinit var biometricUnlockController: BiometricUnlockController
@Mock private lateinit var dozeTransitionListener: DozeTransitionListener
@Mock private lateinit var authController: AuthController
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var dreamOverlayCallbackController: DreamOverlayCallbackController
- @Mock private lateinit var dozeParameters: DozeParameters
private val mainDispatcher = StandardTestDispatcher()
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -89,12 +81,9 @@
underTest =
KeyguardRepositoryImpl(
statusBarStateController,
- screenLifecycle,
- biometricUnlockController,
keyguardStateController,
keyguardUpdateMonitor,
dozeTransitionListener,
- dozeParameters,
authController,
dreamOverlayCallbackController,
mainDispatcher,
@@ -201,26 +190,6 @@
}
@Test
- fun isAodAvailable() = runTest {
- val flow = underTest.isAodAvailable
- val isAodAvailable = collectLastValue(flow)
- runCurrent()
-
- val callback =
- withArgCaptor<DozeParameters.Callback> { verify(dozeParameters).addCallback(capture()) }
-
- whenever(dozeParameters.getAlwaysOn()).thenReturn(false)
- callback.onAlwaysOnChange()
- assertThat(isAodAvailable()).isEqualTo(false)
-
- whenever(dozeParameters.getAlwaysOn()).thenReturn(true)
- callback.onAlwaysOnChange()
- assertThat(isAodAvailable()).isEqualTo(true)
-
- flow.onCompletion { verify(dozeParameters).removeCallback(callback) }
- }
-
- @Test
fun isKeyguardOccluded() =
testScope.runTest {
whenever(keyguardStateController.isOccluded).thenReturn(false)
@@ -386,53 +355,6 @@
}
@Test
- fun biometricUnlockState() =
- testScope.runTest {
- val values = mutableListOf<BiometricUnlockModel>()
- val job = underTest.biometricUnlockState.onEach(values::add).launchIn(this)
-
- runCurrent()
- val captor = argumentCaptor<BiometricUnlockController.BiometricUnlockEventsListener>()
- verify(biometricUnlockController).addListener(captor.capture())
-
- listOf(
- BiometricUnlockController.MODE_NONE,
- BiometricUnlockController.MODE_WAKE_AND_UNLOCK,
- BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING,
- BiometricUnlockController.MODE_SHOW_BOUNCER,
- BiometricUnlockController.MODE_ONLY_WAKE,
- BiometricUnlockController.MODE_UNLOCK_COLLAPSING,
- BiometricUnlockController.MODE_DISMISS_BOUNCER,
- BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM,
- )
- .forEach {
- whenever(biometricUnlockController.mode).thenReturn(it)
- captor.value.onModeChanged(it)
- runCurrent()
- }
-
- assertThat(values)
- .isEqualTo(
- listOf(
- // Initial value will be NONE, followed by onModeChanged() call
- BiometricUnlockModel.NONE,
- BiometricUnlockModel.NONE,
- BiometricUnlockModel.WAKE_AND_UNLOCK,
- BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING,
- BiometricUnlockModel.SHOW_BOUNCER,
- BiometricUnlockModel.ONLY_WAKE,
- BiometricUnlockModel.UNLOCK_COLLAPSING,
- BiometricUnlockModel.DISMISS_BOUNCER,
- BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM,
- )
- )
-
- job.cancel()
- runCurrent()
- verify(biometricUnlockController).removeListener(captor.value)
- }
-
- @Test
fun dozeTransitionModel() =
testScope.runTest {
// For the initial state
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
index 5afc405..ad80a06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
@@ -29,6 +29,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.util.KeyguardTransitionRunner
@@ -86,7 +87,7 @@
}
@Test
- fun startingSecondTransitionWillCancelTheFirstTransition() =
+ fun startingSecondTransitionWillCancelTheFirstTransitionAndUseLastValue() =
TestScope().runTest {
val steps = mutableListOf<TransitionStep>()
val job = underTest.transition(AOD, LOCKSCREEN).onEach { steps.add(it) }.launchIn(this)
@@ -100,12 +101,19 @@
val job2 = underTest.transition(LOCKSCREEN, AOD).onEach { steps.add(it) }.launchIn(this)
runner.startTransition(
this,
- TransitionInfo(OWNER_NAME, LOCKSCREEN, AOD, getAnimator()),
+ TransitionInfo(
+ OWNER_NAME,
+ LOCKSCREEN,
+ AOD,
+ getAnimator(),
+ TransitionModeOnCanceled.LAST_VALUE
+ ),
)
val firstTransitionSteps = listWithStep(step = BigDecimal(.1), stop = BigDecimal(.1))
assertSteps(steps.subList(0, 4), firstTransitionSteps, AOD, LOCKSCREEN)
+ // Second transition starts from .1 (LAST_VALUE)
val secondTransitionSteps = listWithStep(step = BigDecimal(.1), start = BigDecimal(.1))
assertSteps(steps.subList(4, steps.size), secondTransitionSteps, LOCKSCREEN, AOD)
@@ -114,6 +122,76 @@
}
@Test
+ fun startingSecondTransitionWillCancelTheFirstTransitionAndUseReset() =
+ TestScope().runTest {
+ val steps = mutableListOf<TransitionStep>()
+ val job = underTest.transition(AOD, LOCKSCREEN).onEach { steps.add(it) }.launchIn(this)
+ runner.startTransition(
+ this,
+ TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, getAnimator()),
+ maxFrames = 3,
+ )
+
+ // Now start 2nd transition, which will interrupt the first
+ val job2 = underTest.transition(LOCKSCREEN, AOD).onEach { steps.add(it) }.launchIn(this)
+ runner.startTransition(
+ this,
+ TransitionInfo(
+ OWNER_NAME,
+ LOCKSCREEN,
+ AOD,
+ getAnimator(),
+ TransitionModeOnCanceled.RESET
+ ),
+ )
+
+ val firstTransitionSteps = listWithStep(step = BigDecimal(.1), stop = BigDecimal(.1))
+ assertSteps(steps.subList(0, 4), firstTransitionSteps, AOD, LOCKSCREEN)
+
+ // Second transition starts from 0 (RESET)
+ val secondTransitionSteps = listWithStep(start = BigDecimal(0), step = BigDecimal(.1))
+ assertSteps(steps.subList(4, steps.size), secondTransitionSteps, LOCKSCREEN, AOD)
+
+ job.cancel()
+ job2.cancel()
+ }
+
+ @Test
+ fun startingSecondTransitionWillCancelTheFirstTransitionAndUseReverse() =
+ TestScope().runTest {
+ val steps = mutableListOf<TransitionStep>()
+ val job = underTest.transition(AOD, LOCKSCREEN).onEach { steps.add(it) }.launchIn(this)
+ runner.startTransition(
+ this,
+ TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, getAnimator()),
+ maxFrames = 3,
+ )
+
+ // Now start 2nd transition, which will interrupt the first
+ val job2 = underTest.transition(LOCKSCREEN, AOD).onEach { steps.add(it) }.launchIn(this)
+ runner.startTransition(
+ this,
+ TransitionInfo(
+ OWNER_NAME,
+ LOCKSCREEN,
+ AOD,
+ getAnimator(),
+ TransitionModeOnCanceled.REVERSE
+ ),
+ )
+
+ val firstTransitionSteps = listWithStep(step = BigDecimal(.1), stop = BigDecimal(.1))
+ assertSteps(steps.subList(0, 4), firstTransitionSteps, AOD, LOCKSCREEN)
+
+ // Second transition starts from .9 (REVERSE)
+ val secondTransitionSteps = listWithStep(start = BigDecimal(0.9), step = BigDecimal(.1))
+ assertSteps(steps.subList(4, steps.size), secondTransitionSteps, LOCKSCREEN, AOD)
+
+ job.cancel()
+ job2.cancel()
+ }
+
+ @Test
fun nullAnimatorEnablesManualControlWithUpdateTransition() =
TestScope().runTest {
val steps = mutableListOf<TransitionStep>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
index 5bd747f..8dea57c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
@@ -20,10 +20,14 @@
import androidx.test.filters.SmallTest
import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.mock
import dagger.Lazy
import junit.framework.Assert.assertEquals
@@ -42,6 +46,12 @@
class FromPrimaryBouncerTransitionInteractorTest : KeyguardTransitionInteractorTestCase() {
private lateinit var underTest: FromPrimaryBouncerTransitionInteractor
+ private val mSelectedUserInteractor =
+ SelectedUserInteractor(
+ FakeUserRepository(),
+ FakeFeatureFlagsClassic().apply { set(Flags.REFACTOR_GETCURRENTUSER, true) }
+ )
+
// Override the fromPrimaryBouncerTransitionInteractor provider from the superclass so our
// underTest interactor is provided to any classes that need it.
override var fromPrimaryBouncerTransitionInteractorLazy:
@@ -63,6 +73,7 @@
flags = FakeFeatureFlags(),
keyguardSecurityModel = mock(),
powerInteractor = PowerInteractorFactory.create().powerInteractor,
+ selectedUserInteractor = mSelectedUserInteractor
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
index d6e19cb..e87adf5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
@@ -64,8 +64,6 @@
KeyguardDismissInteractorFactory.create(
context = context,
testScope = testScope,
- broadcastDispatcher = fakeBroadcastDispatcher,
- dispatcher = dispatcher,
)
keyguardRepository = dismissInteractorWithDependencies.keyguardRepository
transitionRepository = FakeKeyguardTransitionRepository()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt
index a5cfbbf..ecb46bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt
@@ -58,8 +58,6 @@
KeyguardDismissInteractorFactory.create(
context = context,
testScope = testScope,
- broadcastDispatcher = fakeBroadcastDispatcher,
- dispatcher = dispatcher,
)
underTest = underTestDependencies.interactor
underTestDependencies.userRepository.setUserInfos(listOf(userInfo))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index 6ad2d73..e45f56a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -62,6 +62,7 @@
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -99,6 +100,7 @@
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var faceWakeUpTriggersConfig: FaceWakeUpTriggersConfig
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Before
fun setup() {
@@ -145,6 +147,7 @@
keyguardUpdateMonitor,
FakeTrustRepository(),
testScope.backgroundScope,
+ mSelectedUserInteractor,
),
AlternateBouncerInteractor(
mock(StatusBarStateController::class.java),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 5d5ece0..275ac80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -42,6 +42,7 @@
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.domain.model.ShadeModel
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
@@ -56,7 +57,6 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
-import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.clearInvocations
@@ -86,6 +86,7 @@
// Used to verify transition requests for test output
@Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
private lateinit var fromDreamingTransitionInteractor: FromDreamingTransitionInteractor
@@ -158,6 +159,7 @@
flags = featureFlags,
keyguardSecurityModel = keyguardSecurityModel,
powerInteractor = powerInteractor,
+ selectedUserInteractor = mSelectedUserInteractor,
)
.apply { start() }
@@ -241,7 +243,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to PRIMARY_BOUNCER should occur
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -268,7 +270,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
@@ -295,7 +297,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
@@ -325,7 +327,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to DREAMING should occur
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -356,7 +358,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to DREAMING_LOCKSCREEN_HOSTED should occur
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -383,7 +385,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -410,7 +412,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -443,7 +445,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to Lockscreen should occur
assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
@@ -471,7 +473,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to Gone should occur
assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
@@ -501,7 +503,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to PRIMARY_BOUNCER should occur
assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
@@ -534,7 +536,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
@@ -566,7 +568,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to OCCLUDED should occur
assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
@@ -590,7 +592,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor")
@@ -622,7 +624,7 @@
advanceUntilIdle()
// THEN the transition is ignored
- verify(transitionRepository, never()).startTransition(any(), anyBoolean())
+ verify(transitionRepository, never()).startTransition(any())
coroutineContext.cancelChildren()
}
@@ -639,7 +641,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor")
@@ -666,7 +668,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -693,7 +695,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to AOD should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -716,7 +718,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to AOD should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -746,7 +748,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to DREAMING should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -777,7 +779,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to DREAMING_LOCKSCREEN_HOSTED should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -803,7 +805,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to PRIMARY_BOUNCER should occur
assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -835,7 +837,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to AOD should occur
assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -868,7 +870,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -898,7 +900,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to LOCKSCREEN should occur
assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -926,7 +928,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to AOD should occur
assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
@@ -954,7 +956,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
@@ -978,7 +980,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to LOCKSCREEN should occur
assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
@@ -1008,7 +1010,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition back to DREAMING_LOCKSCREEN_HOSTED should occur
assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
@@ -1039,7 +1041,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to GONE should occur
assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
@@ -1068,7 +1070,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to LOCKSCREEN should occur
assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
@@ -1093,7 +1095,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to AlternateBouncer should occur
assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
@@ -1118,7 +1120,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to AlternateBouncer should occur
assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
@@ -1144,7 +1146,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to OCCLUDED should occur
assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
@@ -1169,7 +1171,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to OCCLUDED should occur
assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor")
@@ -1196,7 +1198,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to OCCLUDED should occur
assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor")
@@ -1220,7 +1222,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to OCCLUDED should occur
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -1250,7 +1252,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
// THEN a transition to OCCLUDED should occur
assertThat(info.ownerName).isEqualTo("FromAodTransitionInteractor")
@@ -1284,7 +1286,7 @@
// THEN a transition from LOCKSCREEN => OCCLUDED should occur
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
@@ -1315,7 +1317,7 @@
// THEN a transition from LOCKSCREEN => PRIMARY_BOUNCER should occur
val info =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
@@ -1336,7 +1338,7 @@
// THEN a transition from PRIMARY_BOUNCER => LOCKSCREEN should occur
val info2 =
withArgCaptor<TransitionInfo> {
- verify(transitionRepository).startTransition(capture(), anyBoolean())
+ verify(transitionRepository).startTransition(capture())
}
assertThat(info2.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
assertThat(info2.to).isEqualTo(KeyguardState.LOCKSCREEN)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
index 02c98cd..f9362a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
@@ -46,6 +46,7 @@
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -90,6 +91,7 @@
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var mockedContext: Context
@Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Before
fun setup() {
@@ -145,6 +147,7 @@
keyguardUpdateMonitor,
trustRepository,
testScope.backgroundScope,
+ mSelectedUserInteractor
),
AlternateBouncerInteractor(
statusBarStateController = mock(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt
index 54fc493..1abb441 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt
@@ -42,7 +42,7 @@
private var frameCount = 1L
private var frames = MutableStateFlow(Pair<Long, FrameCallback?>(0L, null))
private var job: Job? = null
- private var isTerminated = false
+ @Volatile private var isTerminated = false
/**
* For transitions being directed by an animator. Will control the number of frames being
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index 9b1f830..0e6e4fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -36,7 +36,6 @@
import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig
import com.android.systemui.res.R
import com.android.systemui.security.data.model.SecurityModel
-import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.statusbar.policy.FakeSecurityController
import com.android.systemui.statusbar.policy.FakeUserInfoController
import com.android.systemui.statusbar.policy.FakeUserInfoController.FakeInfo
@@ -130,7 +129,6 @@
val userInfoController = FakeUserInfoController(FakeInfo(picture = picture))
val settings = FakeGlobalSettings()
val userId = 42
- val userTracker = FakeUserTracker(userId)
val userSwitcherControllerWrapper =
MockUserSwitcherControllerWrapper(currentUserName = "foo")
@@ -148,7 +146,6 @@
utils.footerActionsInteractor(
userSwitcherRepository =
utils.userSwitcherRepository(
- userTracker = userTracker,
settings = settings,
userManager = userManager,
userInfoController = userInfoController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
index 0552ced..0e4b113 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
@@ -124,11 +124,44 @@
}
@Test
- fun testLongClickIntent() {
+ fun testLongClickIntent_safetyCenterEnabled() {
whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(true)
- assertThat(tile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_CONTROLS)
+ val cameraTile = CameraToggleTile(
+ host,
+ uiEventLogger,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ metricsLogger,
+ FalsingManagerFake(),
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ privacyController,
+ keyguardStateController,
+ safetyCenterManager)
+ assertThat(cameraTile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_CONTROLS)
+ cameraTile.destroy()
+ testableLooper.processAllMessages()
+ }
+ @Test
+ fun testLongClickIntent_safetyCenterDisabled() {
whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(false)
+ val cameraTile = CameraToggleTile(
+ host,
+ uiEventLogger,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ metricsLogger,
+ FalsingManagerFake(),
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ privacyController,
+ keyguardStateController,
+ safetyCenterManager)
assertThat(tile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_SETTINGS)
+ cameraTile.destroy()
+ testableLooper.processAllMessages()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
index 0fcfdb6..b98a7570 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
@@ -123,11 +123,44 @@
}
@Test
- fun testLongClickIntent() {
+ fun testLongClickIntent_safetyCenterEnabled() {
whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(true)
- assertThat(tile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_CONTROLS)
+ val micTile = MicrophoneToggleTile(
+ host,
+ uiEventLogger,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ metricsLogger,
+ FalsingManagerFake(),
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ privacyController,
+ keyguardStateController,
+ safetyCenterManager)
+ assertThat(micTile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_CONTROLS)
+ micTile.destroy()
+ testableLooper.processAllMessages()
+ }
+ @Test
+ fun testLongClickIntent_safetyCenterDisabled() {
whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(false)
- assertThat(tile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_SETTINGS)
+ val micTile = MicrophoneToggleTile(
+ host,
+ uiEventLogger,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ metricsLogger,
+ FalsingManagerFake(),
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ privacyController,
+ keyguardStateController,
+ safetyCenterManager)
+ assertThat(micTile.longClickIntent?.action).isEqualTo(Settings.ACTION_PRIVACY_SETTINGS)
+ micTile.destroy()
+ testableLooper.processAllMessages()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index 5b3d45b..ac03073 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -22,11 +22,13 @@
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.Dialog;
import android.os.Handler;
import android.service.quicksettings.Tile;
import android.testing.AndroidTestingRunner;
@@ -35,11 +37,11 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -48,6 +50,7 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.res.R;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -89,6 +92,10 @@
private PanelInteractor mPanelInteractor;
@Mock
private QsEventLogger mUiEventLogger;
+ @Mock
+ private MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
+ @Mock
+ private Dialog mPermissionDialogPrompt;
private TestableLooper mTestableLooper;
private ScreenRecordTile mTile;
@@ -116,7 +123,8 @@
mKeyguardDismissUtil,
mKeyguardStateController,
mDialogLaunchAnimator,
- mPanelInteractor
+ mPanelInteractor,
+ mMediaProjectionMetricsLogger
);
mTile.initialize();
@@ -280,4 +288,27 @@
assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_screen_record_icon_off));
}
+ @Test
+ public void showingDialogPrompt_logsMediaProjectionPermissionRequested() {
+ when(mController.isStarting()).thenReturn(false);
+ when(mController.isRecording()).thenReturn(false);
+ when(mController.createScreenRecordDialog(any(), any(), any(), any(), any()))
+ .thenReturn(mPermissionDialogPrompt);
+
+ mTile.handleClick(null /* view */);
+ mTestableLooper.processAllMessages();
+
+ verify(mController).createScreenRecordDialog(any(), eq(mFeatureFlags),
+ eq(mDialogLaunchAnimator), eq(mActivityStarter), any());
+ var onDismissAction = ArgumentCaptor.forClass(ActivityStarter.OnDismissAction.class);
+ verify(mKeyguardDismissUtil).executeWhenUnlocked(
+ onDismissAction.capture(), anyBoolean(), anyBoolean());
+ assertNotNull(onDismissAction.getValue());
+
+ onDismissAction.getValue().onDismiss();
+
+ verify(mPermissionDialogPrompt).show();
+ verify(mMediaProjectionMetricsLogger).notifyPermissionRequestDisplayed();
+ }
+
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
index f1fcee3..9907278 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
@@ -23,11 +23,14 @@
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dump.LogcatEchoTrackerAlways
import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogBufferFactory
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.base.interactor.StateUpdateTrigger
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
@@ -42,6 +45,7 @@
class QSTileLoggerTest : SysuiTestCase() {
@Mock private lateinit var statusBarController: StatusBarStateController
+ @Mock private lateinit var logBufferFactory: LogBufferFactory
private val chattyLogBuffer = LogBuffer("TestChatty", 5, LogcatEchoTrackerAlways())
private val logBuffer = LogBuffer("Test", 1, LogcatEchoTrackerAlways())
@@ -51,10 +55,11 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
+ whenever(logBufferFactory.create(any(), any(), any())).thenReturn(logBuffer)
underTest =
QSTileLogger(
mapOf(TileSpec.create("chatty_tile") to chattyLogBuffer),
- { logBuffer },
+ logBufferFactory,
statusBarController
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 7b13de6..e1345d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -42,6 +42,7 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Assume.assumeTrue
@@ -292,6 +293,7 @@
initialSceneKey = SceneKey.Lockscreen,
authenticationMethod = AuthenticationMethodModel.Pin,
isDeviceUnlocked = false,
+ startsAwake = false
)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
@@ -299,6 +301,7 @@
utils.deviceEntryRepository.setUnlocked(true)
runCurrent()
powerInteractor.setAwakeForTest()
+ runCurrent()
assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
}
@@ -403,42 +406,43 @@
initialSceneKey = SceneKey.Lockscreen,
authenticationMethod = AuthenticationMethodModel.Pin,
isDeviceUnlocked = false,
+ startsAwake = false,
)
underTest.start()
runCurrent()
verify(falsingCollector, never()).onScreenTurningOn()
verify(falsingCollector, never()).onScreenOnFromTouch()
- verify(falsingCollector, never()).onScreenOff()
+ verify(falsingCollector, times(1)).onScreenOff()
powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_POWER_BUTTON)
runCurrent()
verify(falsingCollector, times(1)).onScreenTurningOn()
verify(falsingCollector, never()).onScreenOnFromTouch()
- verify(falsingCollector, never()).onScreenOff()
+ verify(falsingCollector, times(1)).onScreenOff()
powerInteractor.setAsleepForTest()
runCurrent()
verify(falsingCollector, times(1)).onScreenTurningOn()
verify(falsingCollector, never()).onScreenOnFromTouch()
- verify(falsingCollector, times(1)).onScreenOff()
+ verify(falsingCollector, times(2)).onScreenOff()
powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_TAP)
runCurrent()
verify(falsingCollector, times(1)).onScreenTurningOn()
verify(falsingCollector, times(1)).onScreenOnFromTouch()
- verify(falsingCollector, times(1)).onScreenOff()
+ verify(falsingCollector, times(2)).onScreenOff()
powerInteractor.setAsleepForTest()
runCurrent()
verify(falsingCollector, times(1)).onScreenTurningOn()
verify(falsingCollector, times(1)).onScreenOnFromTouch()
- verify(falsingCollector, times(2)).onScreenOff()
+ verify(falsingCollector, times(3)).onScreenOff()
powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_POWER_BUTTON)
runCurrent()
verify(falsingCollector, times(2)).onScreenTurningOn()
verify(falsingCollector, times(1)).onScreenOnFromTouch()
- verify(falsingCollector, times(2)).onScreenOff()
+ verify(falsingCollector, times(3)).onScreenOff()
}
@Test
@@ -509,11 +513,12 @@
verify(falsingCollector, times(2)).onBouncerHidden()
}
- private fun prepareState(
+ private fun TestScope.prepareState(
isDeviceUnlocked: Boolean = false,
isBypassEnabled: Boolean = false,
initialSceneKey: SceneKey? = null,
authenticationMethod: AuthenticationMethodModel? = null,
+ startsAwake: Boolean = true,
): MutableStateFlow<ObservableTransitionState> {
assumeTrue(Flags.SCENE_CONTAINER_ENABLED)
sceneContainerFlags.enabled = true
@@ -537,6 +542,13 @@
authenticationMethod != AuthenticationMethodModel.None
)
}
+ if (startsAwake) {
+ powerInteractor.setAwakeForTest()
+ } else {
+ powerInteractor.setAsleepForTest()
+ }
+ runCurrent()
+
return transitionStateFlow
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index eb00610..be82bc3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -90,7 +90,8 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
-import com.android.systemui.user.domain.interactor.UserInteractor;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
import com.google.common.util.concurrent.MoreExecutors;
@@ -131,6 +132,7 @@
@Mock private AuthController mAuthController;
@Mock private ShadeExpansionStateManager mShadeExpansionStateManager;
@Mock private ShadeWindowLogger mShadeWindowLogger;
+ @Mock private SelectedUserInteractor mSelectedUserInteractor;
@Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters;
@Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener;
private final Executor mBackgroundExecutor = MoreExecutors.directExecutor();
@@ -216,6 +218,7 @@
keyguardInteractor,
featureFlags,
mKeyguardSecurityModel,
+ mSelectedUserInteractor,
powerInteractor);
mShadeInteractor =
@@ -230,7 +233,7 @@
keyguardTransitionInteractor,
powerInteractor,
new FakeUserSetupRepository(),
- mock(UserInteractor.class),
+ mock(UserSwitcherInteractor.class),
new SharedNotificationContainerInteractor(
configurationRepository,
mContext,
@@ -255,7 +258,8 @@
mAuthController,
mShadeExpansionStateManager,
() -> mShadeInteractor,
- mShadeWindowLogger) {
+ mShadeWindowLogger,
+ () -> mSelectedUserInteractor) {
@Override
protected boolean isDebuggable() {
return false;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 677d9db..da49230 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -21,7 +21,9 @@
import android.testing.TestableLooper.RunWithLooper
import android.view.KeyEvent
import android.view.MotionEvent
+import android.view.View
import android.view.ViewGroup
+import androidx.core.view.contains
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardMessageAreaController
import com.android.keyguard.KeyguardSecurityContainerController
@@ -29,6 +31,8 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardBouncerComponent
+import com.android.systemui.FakeFeatureFlagsImpl
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
import com.android.systemui.back.domain.interactor.BackActionInteractor
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
@@ -43,11 +47,19 @@
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.compose.ComposeFacade.isComposeAvailable
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.dump.logcatLogBuffer
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags.ALTERNATE_BOUNCER_VIEW
+import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
+import com.android.systemui.flags.Flags.MIGRATE_NSSL
+import com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES
+import com.android.systemui.flags.Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION
+import com.android.systemui.flags.Flags.TRACKPAD_GESTURE_COMMON
+import com.android.systemui.flags.Flags.TRACKPAD_GESTURE_FEATURES
import com.android.systemui.flags.SystemPropertiesHelper
import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler
import com.android.systemui.keyguard.DismissCallbackRegistry
@@ -81,7 +93,9 @@
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -96,6 +110,7 @@
import org.mockito.Mockito.anyFloat
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import java.util.Optional
@@ -134,6 +149,7 @@
@Mock
private lateinit var mLockscreenHostedDreamGestureListener: LockscreenHostedDreamGestureListener
@Mock private lateinit var notificationInsetsController: NotificationInsetsController
+ @Mock private lateinit var mCommunalViewModel: CommunalViewModel
@Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
@Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
@Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
@@ -142,6 +158,7 @@
Optional<UnfoldTransitionProgressProvider>
@Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@Mock lateinit var dragDownHelper: DragDownHelper
+ @Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock
lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel
@Mock lateinit var sysUIKeyEventHandler: SysUIKeyEventHandler
@@ -157,7 +174,8 @@
private lateinit var testScope: TestScope
- private lateinit var featureFlags: FakeFeatureFlags
+ private lateinit var featureFlagsClassic: FakeFeatureFlagsClassic
+ private lateinit var featureFlags: FakeFeatureFlagsImpl
@Before
fun setUp() {
@@ -172,14 +190,16 @@
whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
.thenReturn(emptyFlow<TransitionStep>())
- featureFlags = FakeFeatureFlags()
- featureFlags.set(Flags.TRACKPAD_GESTURE_COMMON, true)
- featureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false)
- featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
- featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
- featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
- featureFlags.set(Flags.MIGRATE_NSSL, false)
- featureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false)
+ featureFlagsClassic = FakeFeatureFlagsClassic()
+ featureFlagsClassic.set(TRACKPAD_GESTURE_COMMON, true)
+ featureFlagsClassic.set(TRACKPAD_GESTURE_FEATURES, false)
+ featureFlagsClassic.set(SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
+ featureFlagsClassic.set(REVAMPED_BOUNCER_MESSAGES, true)
+ featureFlagsClassic.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
+ featureFlagsClassic.set(MIGRATE_NSSL, false)
+ featureFlagsClassic.set(ALTERNATE_BOUNCER_VIEW, false)
+
+ featureFlags = FakeFeatureFlagsImpl()
testScope = TestScope()
fakeClock = FakeSystemClock()
@@ -200,8 +220,6 @@
centralSurfaces,
dozeServiceHost,
dozeScrimController,
- backActionInteractor,
- powerInteractor,
notificationShadeWindowController,
unfoldTransitionProgressProvider,
keyguardUnlockAnimationController,
@@ -216,14 +234,16 @@
mock(KeyguardMessageAreaController.Factory::class.java),
keyguardTransitionInteractor,
primaryBouncerToGoneTransitionViewModel,
+ mCommunalViewModel,
notificationExpansionRepository,
+ featureFlagsClassic,
featureFlags,
fakeClock,
BouncerMessageInteractor(
repository = BouncerMessageRepositoryImpl(),
userRepository = FakeUserRepository(),
countDownTimerUtil = mock(CountDownTimerUtil::class.java),
- featureFlags = featureFlags,
+ featureFlags = featureFlagsClassic,
updateMonitor = mock(KeyguardUpdateMonitor::class.java),
biometricSettingsRepository = FakeBiometricSettingsRepository(),
applicationScope = testScope.backgroundScope,
@@ -243,6 +263,7 @@
mock(KeyguardUpdateMonitor::class.java),
FakeTrustRepository(),
testScope.backgroundScope,
+ mSelectedUserInteractor,
),
facePropertyRepository = FakeFacePropertyRepository(),
deviceEntryFingerprintAuthRepository =
@@ -254,6 +275,7 @@
sysUIKeyEventHandler,
primaryBouncerInteractor,
alternateBouncerInteractor,
+ mSelectedUserInteractor,
)
underTest.setupExpandedStatusBar()
underTest.setDragDownHelper(dragDownHelper)
@@ -441,7 +463,7 @@
whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT))
.thenReturn(true)
- featureFlags.set(Flags.MIGRATE_NSSL, true)
+ featureFlagsClassic.set(MIGRATE_NSSL, true)
// THEN touch should NOT be intercepted by NotificationShade
assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isFalse()
@@ -458,7 +480,7 @@
whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT))
.thenReturn(false)
- featureFlags.set(Flags.MIGRATE_NSSL, true)
+ featureFlagsClassic.set(MIGRATE_NSSL, true)
// THEN touch should NOT be intercepted by NotificationShade
assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
@@ -472,6 +494,48 @@
}
@Test
+ fun setsUpCommunalHubLayout_whenFlagEnabled() {
+ if (!isComposeAvailable()) {
+ return
+ }
+
+ featureFlags.setFlag(FLAG_COMMUNAL_HUB, true)
+
+ val mockCommunalPlaceholder = mock(View::class.java)
+ val fakeViewIndex = 20
+ whenever(view.findViewById<View>(R.id.communal_ui_stub)).thenReturn(mockCommunalPlaceholder)
+ whenever(view.indexOfChild(mockCommunalPlaceholder)).thenReturn(fakeViewIndex)
+ whenever(view.context).thenReturn(context)
+
+ underTest.setupCommunalHubLayout()
+
+ // Communal view added as a child of the container at the proper index, the stub is removed.
+ verify(view).removeView(mockCommunalPlaceholder)
+ verify(view).addView(any(), eq(fakeViewIndex))
+ }
+
+ @Test
+ fun doesNotSetupCommunalHubLayout_whenFlagDisabled() {
+ if (!isComposeAvailable()) {
+ return
+ }
+
+ featureFlags.setFlag(FLAG_COMMUNAL_HUB, false)
+
+ val mockCommunalPlaceholder = mock(View::class.java)
+ val fakeViewIndex = 20
+ whenever(view.findViewById<View>(R.id.communal_ui_stub)).thenReturn(mockCommunalPlaceholder)
+ whenever(view.indexOfChild(mockCommunalPlaceholder)).thenReturn(fakeViewIndex)
+ whenever(view.context).thenReturn(context)
+
+ underTest.setupCommunalHubLayout()
+
+ // No adding or removing of views occurs.
+ verify(view, times(0)).removeView(mockCommunalPlaceholder)
+ verify(view, times(0)).addView(any(), eq(fakeViewIndex))
+ }
+
+ @Test
fun forwardsDispatchKeyEvent() {
val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B)
interactionEventHandler.dispatchKeyEvent(keyEvent)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index a4a2ca0..c94741f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -28,6 +28,7 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardBouncerComponent
+import com.android.systemui.FakeFeatureFlagsImpl
import com.android.systemui.SysuiTestCase
import com.android.systemui.back.domain.interactor.BackActionInteractor
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
@@ -42,6 +43,7 @@
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.dump.logcatLogBuffer
@@ -79,6 +81,7 @@
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -141,9 +144,11 @@
private lateinit var unfoldTransitionProgressProvider:
Optional<UnfoldTransitionProgressProvider>
@Mock private lateinit var notificationInsetsController: NotificationInsetsController
+ @Mock private lateinit var mCommunalViewModel: CommunalViewModel
@Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@Mock lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
@Mock lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock
private lateinit var primaryBouncerToGoneTransitionViewModel:
PrimaryBouncerToGoneTransitionViewModel
@@ -202,8 +207,6 @@
centralSurfaces,
dozeServiceHost,
dozeScrimController,
- backActionInteractor,
- powerInteractor,
notificationShadeWindowController,
unfoldTransitionProgressProvider,
keyguardUnlockAnimationController,
@@ -218,8 +221,10 @@
Mockito.mock(KeyguardMessageAreaController.Factory::class.java),
keyguardTransitionInteractor,
primaryBouncerToGoneTransitionViewModel,
+ mCommunalViewModel,
NotificationExpansionRepository(),
featureFlags,
+ FakeFeatureFlagsImpl(),
FakeSystemClock(),
BouncerMessageInteractor(
repository = BouncerMessageRepositoryImpl(),
@@ -245,6 +250,7 @@
Mockito.mock(KeyguardUpdateMonitor::class.java),
FakeTrustRepository(),
testScope.backgroundScope,
+ mSelectedUserInteractor,
),
facePropertyRepository = FakeFacePropertyRepository(),
deviceEntryFingerprintAuthRepository =
@@ -256,6 +262,7 @@
Mockito.mock(SysUIKeyEventHandler::class.java),
primaryBouncerInteractor,
alternateBouncerInteractor,
+ mSelectedUserInteractor,
)
controller.setupExpandedStatusBar()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index 65174ba..0fcfaf9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -95,15 +95,17 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
-import com.android.systemui.user.domain.interactor.UserInteractor;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
import com.android.systemui.util.kotlin.JavaAdapter;
+import dagger.Lazy;
+
import org.junit.After;
import org.junit.Before;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import dagger.Lazy;
import kotlinx.coroutines.test.TestScope;
public class QuickSettingsControllerBaseTest extends SysuiTestCase {
@@ -162,7 +164,8 @@
@Mock protected DumpManager mDumpManager;
@Mock protected UiEventLogger mUiEventLogger;
@Mock protected CastController mCastController;
- @Mock protected UserInteractor mUserInteractor;
+ @Mock protected UserSwitcherInteractor mUserSwitcherInteractor;
+ @Mock protected SelectedUserInteractor mSelectedUserInteractor;
protected FakeDisableFlagsRepository mDisableFlagsRepository =
new FakeDisableFlagsRepository();
protected FakeKeyguardRepository mKeyguardRepository = new FakeKeyguardRepository();
@@ -249,6 +252,7 @@
keyguardInteractor,
featureFlags,
mock(KeyguardSecurityModel.class),
+ mSelectedUserInteractor,
powerInteractor);
ResourcesSplitShadeStateController splitShadeStateController =
@@ -266,7 +270,7 @@
keyguardTransitionInteractor,
powerInteractor,
new FakeUserSetupRepository(),
- mUserInteractor,
+ mUserSwitcherInteractor,
new SharedNotificationContainerInteractor(
configurationRepository,
mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
index 5ca45f3..7cb6d93 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
@@ -398,6 +398,12 @@
.isEqualTo(mQsController.getScrimCornerRadius());
}
+ @Test
+ public void disallowTouches_nullQs_false() {
+ mQsController.setQs(null);
+ assertThat(mQsController.disallowTouches()).isFalse();
+ }
+
private void lockScreen() {
mQsController.setBarState(KEYGUARD);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index 81382a4..3a260ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -445,105 +445,6 @@
}
@Test
- fun expanding_shadeDraggedDown_expandingTrue() =
- testScope.runTest() {
- val actual by collectLastValue(underTest.isAnyExpanding)
-
- // GIVEN shade and QS collapsed
- shadeRepository.setLegacyShadeExpansion(0f)
- shadeRepository.setQsExpansion(0f)
- runCurrent()
-
- // WHEN shade partially expanded
- shadeRepository.setLegacyShadeExpansion(.5f)
- runCurrent()
-
- // THEN anyExpanding is true
- assertThat(actual).isTrue()
- }
-
- @Test
- fun expanding_qsDraggedDown_expandingTrue() =
- testScope.runTest() {
- val actual by collectLastValue(underTest.isAnyExpanding)
-
- // GIVEN shade and QS collapsed
- shadeRepository.setLegacyShadeExpansion(0f)
- shadeRepository.setQsExpansion(0f)
- runCurrent()
-
- // WHEN shade partially expanded
- shadeRepository.setQsExpansion(.5f)
- runCurrent()
-
- // THEN anyExpanding is true
- assertThat(actual).isTrue()
- }
-
- @Test
- fun expanding_shadeDraggedUpAndDown() =
- testScope.runTest() {
- val actual by collectLastValue(underTest.isAnyExpanding)
-
- // WHEN shade starts collapsed then partially expanded
- shadeRepository.setLegacyShadeExpansion(0f)
- shadeRepository.setLegacyShadeExpansion(.5f)
- shadeRepository.setQsExpansion(0f)
- runCurrent()
-
- // THEN anyExpanding is true
- assertThat(actual).isTrue()
-
- // WHEN shade dragged up a bit
- shadeRepository.setLegacyShadeExpansion(.2f)
- runCurrent()
-
- // THEN anyExpanding is still true
- assertThat(actual).isTrue()
-
- // WHEN shade dragged down a bit
- shadeRepository.setLegacyShadeExpansion(.7f)
- runCurrent()
-
- // THEN anyExpanding is still true
- assertThat(actual).isTrue()
-
- // WHEN shade fully expanded
- shadeRepository.setLegacyShadeExpansion(1f)
- runCurrent()
-
- // THEN anyExpanding is now false
- assertThat(actual).isFalse()
-
- // WHEN shade dragged up a bit
- shadeRepository.setLegacyShadeExpansion(.7f)
- runCurrent()
-
- // THEN anyExpanding is still false
- assertThat(actual).isFalse()
- }
-
- @Test
- fun expanding_shadeDraggedDownThenUp_expandingFalse() =
- testScope.runTest() {
- val actual by collectLastValue(underTest.isAnyExpanding)
-
- // GIVEN shade starts collapsed
- shadeRepository.setLegacyShadeExpansion(0f)
- shadeRepository.setQsExpansion(0f)
- runCurrent()
-
- // WHEN shade expands but doesn't complete
- shadeRepository.setLegacyShadeExpansion(.5f)
- runCurrent()
- shadeRepository.setLegacyShadeExpansion(0f)
- runCurrent()
-
- // THEN anyExpanding is false
- assertThat(actual).isFalse()
- }
-
- @Test
fun lockscreenShadeExpansion_idle_onScene() =
testScope.runTest() {
// GIVEN an expansion flow based on transitions to and from a scene
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
index c664c39..2ef4374 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
@@ -18,10 +18,16 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.test.suitebuilder.annotation.SmallTest;
@@ -33,8 +39,8 @@
import android.view.animation.Interpolator;
import com.android.app.animation.Interpolators;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.stack.AnimationFilter;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ViewState;
@@ -85,7 +91,7 @@
return mEffectiveProperty;
}
};
- private AnimatorListenerAdapter mFinishListener = mock(AnimatorListenerAdapter.class);
+ private AnimatorListenerAdapter mFinishListener;
private AnimationProperties mAnimationProperties = new AnimationProperties() {
@Override
public AnimationFilter getAnimationFilter() {
@@ -104,6 +110,7 @@
@Before
public void setUp() {
mView = new View(getContext());
+ mFinishListener = mock(AnimatorListenerAdapter.class);
}
@Test
@@ -229,6 +236,32 @@
}
@Test
+ public void testListenerCallbackOrderAndTagState() {
+ mAnimationFilter.reset();
+ mAnimationFilter.animate(mProperty.getProperty());
+ mAnimationProperties.setCustomInterpolator(mEffectiveProperty, mTestInterpolator);
+ mAnimationProperties.setDuration(500);
+
+ // Validates that the onAnimationEnd function set by PropertyAnimator was run first.
+ doAnswer(invocation -> {
+ assertNull(mView.getTag(mProperty.getAnimatorTag()));
+ return null;
+ })
+ .when(mFinishListener)
+ .onAnimationEnd(any(Animator.class), anyBoolean());
+
+ // Begin the animation and verify it set state correctly
+ PropertyAnimator.startAnimation(mView, mProperty, 200f, mAnimationProperties);
+ ValueAnimator animator = ViewState.getChildTag(mView, mProperty.getAnimatorTag());
+ assertNotNull(animator);
+ assertNotNull(mView.getTag(mProperty.getAnimatorTag()));
+
+ // Terminate the animation to run end runners, and validate they executed.
+ animator.end();
+ verify(mFinishListener).onAnimationEnd(animator, false);
+ }
+
+ @Test
public void testIsAnimating() {
mAnimationFilter.reset();
mAnimationFilter.animate(mProperty.getProperty());
@@ -236,4 +269,4 @@
PropertyAnimator.startAnimation(mView, mProperty, 200f, mAnimationProperties);
assertTrue(PropertyAnimator.isAnimating(mView, mProperty));
}
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
index 27be4c8..df547ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
@@ -33,6 +33,7 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
@@ -53,6 +54,7 @@
val keyguardUpdateMonitor: KeyguardUpdateMonitor = mock()
val statusBarStateController: StatusBarStateController = mock()
val keyguardStateController: KeyguardStateController = mock()
+ val mSelectedUserInteractor: SelectedUserInteractor = mock()
val coordinator: SensitiveContentCoordinator =
DaggerTestSensitiveContentCoordinatorComponent
@@ -62,7 +64,8 @@
lockscreenUserManager,
keyguardUpdateMonitor,
statusBarStateController,
- keyguardStateController)
+ keyguardStateController,
+ mSelectedUserInteractor)
.coordinator
@Test
@@ -263,7 +266,8 @@
@BindsInstance lockscreenUserManager: NotificationLockscreenUserManager,
@BindsInstance keyguardUpdateMonitor: KeyguardUpdateMonitor,
@BindsInstance statusBarStateController: StatusBarStateController,
- @BindsInstance keyguardStateController: KeyguardStateController
+ @BindsInstance keyguardStateController: KeyguardStateController,
+ @BindsInstance selectedUserInteractor: SelectedUserInteractor,
): TestSensitiveContentCoordinatorComponent
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index ac11ff2..0a7dc4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -192,10 +192,26 @@
val isOnLockscreen by collectLastValue(underTest.isOnLockscreen)
keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(to = KeyguardState.GONE, transitionState = TransitionState.FINISHED)
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ value = 1f,
+ transitionState = TransitionState.FINISHED
+ )
)
assertThat(isOnLockscreen).isFalse()
+ // While progressing from lockscreen, should still be true
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ value = 0.8f,
+ transitionState = TransitionState.RUNNING
+ )
+ )
+ assertThat(isOnLockscreen).isTrue()
+
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
to = KeyguardState.LOCKSCREEN,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 8344cd1..cfd220b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -52,14 +52,15 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.keyguard.domain.interactor.BiometricUnlockInteractor;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
@@ -109,8 +110,6 @@
@Mock
private WakefulnessLifecycle mWakefulnessLifecycle;
@Mock
- private ScreenLifecycle mScreenLifecycle;
- @Mock
private StatusBarStateController mStatusBarStateController;
@Mock
private SessionTracker mSessionTracker;
@@ -126,6 +125,10 @@
private ViewRootImpl mViewRootImpl;
@Mock
private DeviceEntryHapticsInteractor mDeviceEntryHapticsInteractor;
+ @Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
+ @Mock
+ private BiometricUnlockInteractor mBiometricUnlockInteractor;
private final FakeSystemClock mSystemClock = new FakeSystemClock();
private FakeFeatureFlags mFeatureFlags;
private BiometricUnlockController mBiometricUnlockController;
@@ -163,7 +166,9 @@
mSessionTracker, mLatencyTracker, mScreenOffAnimationController, mVibratorHelper,
mSystemClock,
mFeatureFlags,
- mDeviceEntryHapticsInteractor
+ mDeviceEntryHapticsInteractor,
+ () -> mSelectedUserInteractor,
+ mBiometricUnlockInteractor
);
biometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
biometricUnlockController.addListener(mBiometricUnlockEventsListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index f5b7ca8..6fecbb0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -23,7 +23,6 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -44,6 +43,7 @@
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -85,6 +85,7 @@
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private ConfigurationController mConfigurationController;
@Mock private UserTracker mUserTracker;
+ @Mock private DozeInteractor mDozeInteractor;
@Captor private ArgumentCaptor<BatteryStateChangeCallback> mBatteryStateChangeCallback;
/**
@@ -128,7 +129,8 @@
mKeyguardUpdateMonitor,
mConfigurationController,
mStatusBarStateController,
- mUserTracker
+ mUserTracker,
+ mDozeInteractor
);
verify(mBatteryController).addCallback(mBatteryStateChangeCallback.capture());
@@ -186,9 +188,7 @@
@Test
public void testGetAlwaysOn_whenBatterySaverCallback() {
- DozeParameters.Callback callback = mock(DozeParameters.Callback.class);
- mDozeParameters.addCallback(callback);
-
+ reset(mDozeInteractor);
when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
when(mBatteryController.isAodPowerSave()).thenReturn(true);
@@ -196,16 +196,16 @@
mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
mBatteryStateChangeCallback.getValue().onPowerSaveChanged(true);
- verify(callback, times(2)).onAlwaysOnChange();
+ verify(mDozeInteractor, times(2)).setAodAvailable(anyBoolean());
verify(mScreenOffAnimationController, times(2)).onAlwaysOnChanged(false);
assertThat(mDozeParameters.getAlwaysOn()).isFalse();
reset(mScreenOffAnimationController);
- reset(callback);
+ reset(mDozeInteractor);
when(mBatteryController.isAodPowerSave()).thenReturn(false);
mBatteryStateChangeCallback.getValue().onPowerSaveChanged(true);
- verify(callback).onAlwaysOnChange();
+ verify(mDozeInteractor).setAodAvailable(anyBoolean());
verify(mScreenOffAnimationController).onAlwaysOnChanged(true);
assertThat(mDozeParameters.getAlwaysOn()).isTrue();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 45e9224..46b3996 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -93,6 +93,7 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.google.common.truth.Truth;
@@ -147,6 +148,7 @@
@Mock private WindowInsetsController mWindowInsetsController;
@Mock private TaskbarDelegate mTaskbarDelegate;
@Mock private StatusBarKeyguardViewManager.KeyguardViewManagerCallback mCallback;
+ @Mock private SelectedUserInteractor mSelectedUserInteractor;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback
@@ -212,7 +214,8 @@
mock(KeyguardTransitionInteractor.class),
StandardTestDispatcher(null, null),
() -> mock(WindowManagerLockscreenVisibilityInteractor.class),
- () -> mock(KeyguardDismissActionInteractor.class)) {
+ () -> mock(KeyguardDismissActionInteractor.class),
+ mSelectedUserInteractor) {
@Override
public ViewRootImpl getViewRootImpl() {
return mViewRootImpl;
@@ -715,7 +718,8 @@
mock(KeyguardTransitionInteractor.class),
StandardTestDispatcher(null, null),
() -> mock(WindowManagerLockscreenVisibilityInteractor.class),
- () -> mock(KeyguardDismissActionInteractor.class)) {
+ () -> mock(KeyguardDismissActionInteractor.class),
+ mSelectedUserInteractor) {
@Override
public ViewRootImpl getViewRootImpl() {
return mViewRootImpl;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
index cae892f..e6b09e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
@@ -38,6 +38,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import dagger.Lazy;
@@ -67,6 +68,8 @@
@Mock
private Lazy<KeyguardUnlockAnimationController> mKeyguardUnlockAnimationControllerLazy;
@Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
+ @Mock
private KeyguardUpdateMonitorLogger mLogger;
@Mock
private FeatureFlags mFeatureFlags;
@@ -84,7 +87,8 @@
mKeyguardUnlockAnimationControllerLazy,
mLogger,
mDumpManager,
- mFeatureFlags);
+ mFeatureFlags,
+ mSelectedUserInteractor);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
new file mode 100644
index 0000000..60fe7d2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
@@ -0,0 +1,50 @@
+package com.android.systemui.user.domain.interactor
+
+import android.content.pm.UserInfo
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class SelectedUserInteractorTest : SysuiTestCase() {
+
+ private lateinit var underTest: SelectedUserInteractor
+
+ private val userRepository = FakeUserRepository()
+
+ @Before
+ fun setUp() {
+ userRepository.setUserInfos(USER_INFOS)
+ underTest =
+ SelectedUserInteractor(
+ userRepository,
+ FakeFeatureFlagsClassic().apply { set(Flags.REFACTOR_GETCURRENTUSER, true) }
+ )
+ }
+
+ @Test
+ fun getSelectedUserIdReturnsId() {
+ runBlocking { userRepository.setSelectedUserInfo(USER_INFOS[0]) }
+
+ val actualId = underTest.getSelectedUserId()
+
+ assertThat(actualId).isEqualTo(USER_INFOS[0].id)
+ }
+
+ companion object {
+ private val USER_INFOS =
+ listOf(
+ UserInfo(100, "First user", 0),
+ UserInfo(101, "Second user", 0),
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
index c56266d..1968d75 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
@@ -88,7 +88,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
-class UserInteractorTest : SysuiTestCase() {
+class UserSwitcherInteractorTest : SysuiTestCase() {
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var manager: UserManager
@@ -102,7 +102,7 @@
@Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
- private lateinit var underTest: UserInteractor
+ private lateinit var underTest: UserSwitcherInteractor
private lateinit var spyContext: Context
private lateinit var testScope: TestScope
@@ -665,8 +665,8 @@
userRepository.setUserInfos(userInfos)
userRepository.setSelectedUserInfo(userInfos[0])
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
- val callback1: UserInteractor.UserCallback = mock()
- val callback2: UserInteractor.UserCallback = mock()
+ val callback1: UserSwitcherInteractor.UserCallback = mock()
+ val callback2: UserSwitcherInteractor.UserCallback = mock()
underTest.addCallback(callback1)
underTest.addCallback(callback2)
runCurrent()
@@ -1117,7 +1117,7 @@
userRepository.setSelectedUserInfo(userInfo)
}
underTest =
- UserInteractor(
+ UserSwitcherInteractor(
applicationContext = spyContext,
repository = userRepository,
activityStarter = activityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index a8db368..7041eab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -42,7 +42,7 @@
import com.android.systemui.user.domain.interactor.GuestUserInteractor
import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -243,9 +243,8 @@
userRepository.setSelectedUserInfo(USER_0)
}
return StatusBarUserChipViewModel(
- context = context,
interactor =
- UserInteractor(
+ UserSwitcherInteractor(
applicationContext = context,
repository = userRepository,
activityStarter = activityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index c236b12..686f492 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -42,7 +42,7 @@
import com.android.systemui.user.domain.interactor.GuestUserInteractor
import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
import com.android.systemui.user.shared.model.UserActionModel
import com.android.systemui.util.mockito.any
@@ -157,8 +157,8 @@
underTest =
UserSwitcherViewModel(
- userInteractor =
- UserInteractor(
+ userSwitcherInteractor =
+ UserSwitcherInteractor(
applicationContext = context,
repository = userRepository,
activityStarter = activityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/EventsTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/EventsTest.java
index a853f1d..c69f5c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/EventsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/EventsTest.java
@@ -209,6 +209,11 @@
new int[]{MetricsEvent.POWER_OVERHEAT_ALARM,
MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM},
Events.VolumeDialogEvent.USB_OVERHEAT_ALARM_DISMISSED},
+ {Events.EVENT_ODI_CAPTIONS_CLICK, null, "writeEvent odi_captions_click", null,
+ Events.VolumeDialogEvent.VOLUME_DIALOG_ODI_CAPTIONS_CLICKED},
+ {Events.EVENT_ODI_CAPTIONS_TOOLTIP_CLICK, null,
+ "writeEvent odi_captions_tooltip_click", null,
+ Events.VolumeDialogEvent.VOLUME_DIALOG_ODI_CAPTIONS_TOOLTIP_CLICKED}
});
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index b8f747b..c4c7472 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -53,11 +53,12 @@
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
+import androidx.test.core.view.MotionEventBuilder;
import androidx.test.filters.SmallTest;
import com.android.internal.jank.InteractionJankMonitor;
+import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.Prefs;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.AnimatorTestRule;
import com.android.systemui.dump.DumpManager;
@@ -66,6 +67,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.plugins.VolumeDialogController.State;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DevicePostureController;
@@ -76,6 +78,8 @@
import dagger.Lazy;
+import junit.framework.Assert;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -699,6 +703,60 @@
}
}
+ /**
+ * The click should be a single tap, thus we inject a down and an up event.
+ */
+ @Test
+ public void clickCaptionsButton_logsUiEvent() {
+ UiEventLoggerFake logger = new UiEventLoggerFake();
+ Events.sUiEventLogger = logger;
+ MotionEvent down = MotionEventBuilder.newBuilder()
+ .setAction(MotionEvent.ACTION_DOWN).build();
+ MotionEvent up = MotionEventBuilder.newBuilder()
+ .setAction(MotionEvent.ACTION_UP).build();
+
+ mODICaptionsIcon.onTouchEvent(down);
+ mODICaptionsIcon.onTouchEvent(up);
+ mTestableLooper.moveTimeForward(300); // to confirm it was only a single tap
+ mTestableLooper.processAllMessages();
+
+ boolean foundCaptionLog = false;
+ for (UiEventLoggerFake.FakeUiEvent event : logger.getLogs()) {
+ if (event.eventId
+ == Events.VolumeDialogEvent.VOLUME_DIALOG_ODI_CAPTIONS_CLICKED.getId()) {
+ foundCaptionLog = true;
+ break;
+ }
+ }
+ Assert.assertTrue("Did not log the captions button click.", foundCaptionLog);
+ }
+
+ /**
+ * Pressing the small x button at top right dismisses the captions tooltip.
+ */
+ @Test
+ public void dismissCaptionsTooltip_logsUiEvent() {
+ UiEventLoggerFake logger = new UiEventLoggerFake();
+ Events.sUiEventLogger = logger;
+ mDialog.showCaptionsTooltip();
+ assumeNotNull(mDialog.mODICaptionsTooltipView);
+ View dismissButton = mDialog.mODICaptionsTooltipView.findViewById(R.id.dismiss);
+
+ dismissButton.performClick();
+
+ boolean foundCaptionLog = false;
+ for (UiEventLoggerFake.FakeUiEvent event : logger.getLogs()) {
+ if (event.eventId
+ == Events.VolumeDialogEvent.VOLUME_DIALOG_ODI_CAPTIONS_TOOLTIP_CLICKED.getId()
+ ) {
+ foundCaptionLog = true;
+ break;
+ }
+ }
+ Assert.assertTrue("Did not log the captions tooltip dismiss button click.",
+ foundCaptionLog);
+ }
+
@After
public void teardown() {
// Detailed logs to track down timeout issues in b/299491332
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index c832702..a42fa41 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -156,7 +156,8 @@
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
-import com.android.systemui.user.domain.interactor.UserInteractor;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.bubbles.Bubble;
@@ -324,6 +325,8 @@
@Mock
private ShadeWindowLogger mShadeWindowLogger;
@Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
+ @Mock
private NotifPipelineFlags mNotifPipelineFlags;
@Mock
private Icon mAppBubbleIcon;
@@ -433,6 +436,7 @@
keyguardInteractor,
featureFlags,
mock(KeyguardSecurityModel.class),
+ mSelectedUserInteractor,
powerInteractor);
ResourcesSplitShadeStateController splitShadeStateController =
@@ -450,7 +454,7 @@
keyguardTransitionInteractor,
powerInteractor,
new FakeUserSetupRepository(),
- mock(UserInteractor.class),
+ mock(UserSwitcherInteractor.class),
new SharedNotificationContainerInteractor(
configurationRepository,
mContext,
@@ -476,7 +480,8 @@
mAuthController,
mShadeExpansionStateManager,
() -> mShadeInteractor,
- mShadeWindowLogger
+ mShadeWindowLogger,
+ () -> mSelectedUserInteractor
);
mNotificationShadeWindowController.fetchWindowRootView();
mNotificationShadeWindowController.attach();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
index 30132f7..08adda3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -1,7 +1,8 @@
package com.android.systemui.communal.data.repository
import com.android.systemui.communal.data.model.CommunalWidgetMetadata
-import com.android.systemui.communal.shared.CommunalAppWidgetInfo
+import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -11,7 +12,14 @@
override val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?> = _stopwatchAppWidgetInfo
override var communalWidgetAllowlist: List<CommunalWidgetMetadata> = emptyList()
+ private val _communalWidgets = MutableStateFlow<List<CommunalWidgetContentModel>>(emptyList())
+ override val communalWidgets: Flow<List<CommunalWidgetContentModel>> = _communalWidgets
+
fun setStopwatchAppWidgetInfo(appWidgetInfo: CommunalAppWidgetInfo) {
_stopwatchAppWidgetInfo.value = appWidgetInfo
}
+
+ fun setCommunalWidgets(inventory: List<CommunalWidgetContentModel>) {
+ _communalWidgets.value = inventory
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 9de7ad8..fae49b1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -29,12 +29,12 @@
import com.android.systemui.keyguard.shared.model.StatusBarState
import dagger.Binds
import dagger.Module
+import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import javax.inject.Inject
/** Fake implementation of [KeyguardRepository] */
@SysUISingleton
@@ -182,8 +182,8 @@
_lastDozeTapToWakePosition.value = position
}
- fun setAodAvailable(isAodAvailable: Boolean) {
- _isAodAvailable.value = isAodAvailable
+ override fun setAodAvailable(value: Boolean) {
+ _isAodAvailable.value = value
}
fun setDreaming(isDreaming: Boolean) {
@@ -202,8 +202,8 @@
_dozeAmount.value = dozeAmount
}
- fun setBiometricUnlockState(state: BiometricUnlockModel) {
- _biometricUnlockState.tryEmit(state)
+ override fun setBiometricUnlockState(value: BiometricUnlockModel) {
+ _biometricUnlockState.tryEmit(value)
}
fun setBiometricUnlockSource(source: BiometricUnlockSource?) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index e160548..71e2bc1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -42,7 +42,7 @@
_transitions.emit(step)
}
- override fun startTransition(info: TransitionInfo, resetIfCanceled: Boolean): UUID? {
+ override fun startTransition(info: TransitionInfo): UUID? {
return if (info.animator == null) UUID.randomUUID() else null
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
index 8e96b52..fc34903 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
@@ -16,11 +16,8 @@
package com.android.systemui.keyguard.domain.interactor
-import android.app.ActivityManager
import android.content.Context
import android.os.Handler
-import android.os.UserManager
-import com.android.internal.logging.UiEventLogger
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
@@ -28,7 +25,6 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.ui.BouncerView
-import com.android.systemui.broadcast.FakeBroadcastDispatcher
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
@@ -36,21 +32,13 @@
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
-import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.user.domain.interactor.GuestUserInteractor
-import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
-import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.time.FakeSystemClock
-import com.android.systemui.utils.UserRestrictionChecker
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.test.TestScope
import org.mockito.Mockito.mock
@@ -64,8 +52,6 @@
fun create(
context: Context,
testScope: TestScope,
- broadcastDispatcher: FakeBroadcastDispatcher,
- dispatcher: CoroutineDispatcher,
trustRepository: FakeTrustRepository = FakeTrustRepository(),
keyguardRepository: FakeKeyguardRepository = FakeKeyguardRepository(),
bouncerRepository: FakeKeyguardBouncerRepository = FakeKeyguardBouncerRepository(),
@@ -74,6 +60,7 @@
FakeFeatureFlagsClassic().apply {
set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, true)
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+ set(Flags.REFACTOR_GETCURRENTUSER, true)
},
powerRepository: FakePowerRepository = FakePowerRepository(),
userRepository: FakeUserRepository = FakeUserRepository(),
@@ -92,6 +79,7 @@
keyguardUpdateMonitor,
trustRepository,
testScope.backgroundScope,
+ mock(SelectedUserInteractor::class.java),
)
val alternateBouncerInteractor =
AlternateBouncerInteractor(
@@ -103,38 +91,11 @@
keyguardUpdateMonitor,
)
val powerInteractorWithDeps =
- PowerInteractorFactory.create(
- repository = powerRepository,
- )
- val userInteractor =
- UserInteractor(
- applicationContext = context,
- repository = userRepository,
- mock(ActivityStarter::class.java),
- keyguardInteractor =
- KeyguardInteractorFactory.create(
- repository = keyguardRepository,
- bouncerRepository = bouncerRepository,
- featureFlags = featureFlags,
- )
- .keyguardInteractor,
- featureFlags = featureFlags,
- manager = mock(UserManager::class.java),
- headlessSystemUserMode = mock(HeadlessSystemUserMode::class.java),
- applicationScope = testScope.backgroundScope,
- telephonyInteractor =
- TelephonyInteractor(
- repository = FakeTelephonyRepository(),
- ),
- broadcastDispatcher = broadcastDispatcher,
- keyguardUpdateMonitor = keyguardUpdateMonitor,
- backgroundDispatcher = dispatcher,
- activityManager = mock(ActivityManager::class.java),
- refreshUsersScheduler = mock(RefreshUsersScheduler::class.java),
- guestUserInteractor = mock(GuestUserInteractor::class.java),
- uiEventLogger = mock(UiEventLogger::class.java),
- userRestrictionChecker = mock(UserRestrictionChecker::class.java),
+ PowerInteractorFactory.create(
+ repository = powerRepository,
)
+ val selectedUserInteractor =
+ SelectedUserInteractor(repository = userRepository, flags = featureFlags)
return WithDependencies(
trustRepository = trustRepository,
keyguardRepository = keyguardRepository,
@@ -149,7 +110,7 @@
primaryBouncerInteractor,
alternateBouncerInteractor,
powerInteractorWithDeps.powerInteractor,
- userInteractor,
+ selectedUserInteractor,
),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
index 911eafa..cddb007 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
@@ -41,17 +41,17 @@
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.security.data.repository.SecurityRepository
import com.android.systemui.security.data.repository.SecurityRepositoryImpl
-import com.android.systemui.settings.FakeUserTracker
-import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.FakeSecurityController
import com.android.systemui.statusbar.policy.FakeUserInfoController
import com.android.systemui.statusbar.policy.SecurityController
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.user.data.repository.UserSwitcherRepository
import com.android.systemui.user.data.repository.UserSwitcherRepositoryImpl
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.settings.FakeGlobalSettings
import com.android.systemui.util.settings.GlobalSettings
@@ -102,7 +102,7 @@
deviceProvisionedController: DeviceProvisionedController = mock(),
qsSecurityFooterUtils: QSSecurityFooterUtils = mock(),
fgsManagerController: FgsManagerController = mock(),
- userInteractor: UserInteractor = mock(),
+ userSwitcherInteractor: UserSwitcherInteractor = mock(),
securityRepository: SecurityRepository = securityRepository(),
foregroundServicesRepository: ForegroundServicesRepository = foregroundServicesRepository(),
userSwitcherRepository: UserSwitcherRepository = userSwitcherRepository(),
@@ -116,7 +116,7 @@
deviceProvisionedController,
qsSecurityFooterUtils,
fgsManagerController,
- userInteractor,
+ userSwitcherInteractor,
securityRepository,
foregroundServicesRepository,
userSwitcherRepository,
@@ -149,7 +149,7 @@
bgHandler: Handler = Handler(testableLooper.looper),
bgDispatcher: CoroutineDispatcher = StandardTestDispatcher(scheduler),
userManager: UserManager = mock(),
- userTracker: UserTracker = FakeUserTracker(),
+ userRepository: UserRepository = FakeUserRepository(),
userSwitcherController: UserSwitcherController = mock(),
userInfoController: UserInfoController = FakeUserInfoController(),
settings: GlobalSettings = FakeGlobalSettings(),
@@ -159,10 +159,10 @@
bgHandler,
bgDispatcher,
userManager,
- userTracker,
userSwitcherController,
userInfoController,
settings,
+ userRepository,
)
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
index 4307ff9..7494ccf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
@@ -69,10 +69,16 @@
_userId = _userInfo.id
_userHandle = UserHandle.of(_userId)
+ onBeforeUserSwitching()
onUserChanging()
onUserChanged()
}
+ fun onBeforeUserSwitching(userId: Int = _userId) {
+ val copy = callbacks.toList()
+ copy.forEach { it.onBeforeUserSwitching(userId) }
+ }
+
fun onUserChanging(userId: Int = _userId) {
val copy = callbacks.toList()
copy.forEach { it.onUserChanging(userId, userContext) {} }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/sensors/FakeSensorManager.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/sensors/FakeSensorManager.java
index 197873f..288dcfe 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/sensors/FakeSensorManager.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/sensors/FakeSensorManager.java
@@ -186,7 +186,7 @@
}
@Override
- protected boolean initDataInjectionImpl(boolean enable) {
+ protected boolean initDataInjectionImpl(boolean enable, @DataInjectionMode int mode) {
return false;
}
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index f09cb19..1a735f8 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -43,6 +43,13 @@
}
flag {
+ name: "disable_continuous_shortcut_on_force_stop"
+ namespace: "accessibility"
+ description: "When a package is force stopped, remove the button shortcuts of any continuously-running shortcuts."
+ bug: "198018180"
+}
+
+flag {
name: "deprecate_package_list_observer"
namespace: "accessibility"
description: "Stops using the deprecated PackageListObserver."
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index cd83f8f..5af80da 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -68,7 +68,7 @@
*
* @see #setUserAndEnabledFeatures(int, int)
*/
- static final int FLAG_FEATURE_SCREEN_MAGNIFIER = 0x00000001;
+ static final int FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP = 0x00000001;
/**
* Flag for enabling the touch exploration feature.
@@ -100,7 +100,7 @@
/**
* Flag for enabling the feature to control the screen magnifier. If
- * {@link #FLAG_FEATURE_SCREEN_MAGNIFIER} is set this flag is ignored
+ * {@link #FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP} is set this flag is ignored
* as the screen magnifier feature performs a super set of the work
* performed by this feature.
*
@@ -149,7 +149,7 @@
FLAG_FEATURE_INJECT_MOTION_EVENTS
| FLAG_FEATURE_AUTOCLICK
| FLAG_FEATURE_TOUCH_EXPLORATION
- | FLAG_FEATURE_SCREEN_MAGNIFIER
+ | FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP
| FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER
| FLAG_SERVICE_HANDLES_DOUBLE_TAP
| FLAG_REQUEST_MULTI_FINGER_GESTURES
@@ -530,7 +530,7 @@
}
if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
- || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0)
+ || ((mEnabledFeatures & FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP) != 0)
|| ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
final MagnificationGestureHandler magnificationGestureHandler =
createMagnificationGestureHandler(displayId,
@@ -648,7 +648,7 @@
private MagnificationGestureHandler createMagnificationGestureHandler(
int displayId, Context displayContext) {
final boolean detectControlGestures = (mEnabledFeatures
- & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0;
+ & FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP) != 0;
final boolean triggerable = (mEnabledFeatures
& FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0;
MagnificationGestureHandler magnificationGestureHandler;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 8c1d444..e65a185 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER_CLIENT;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT;
@@ -182,6 +183,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
@@ -650,6 +652,16 @@
}
}
+ /**
+ * Returns the lock object for any synchronized test blocks.
+ * Should not be used outside of testing.
+ * @return lock object.
+ */
+ @VisibleForTesting
+ Object getLock() {
+ return mLock;
+ }
+
AccessibilityUserState getCurrentUserState() {
synchronized (mLock) {
return getCurrentUserStateLocked();
@@ -746,6 +758,62 @@
}
}
+ /**
+ * Handles a package or packages being force stopped.
+ * Will disable any relevant services,
+ * and remove any button targets of continuous services,
+ * denoted by {@link AccessibilityServiceInfo#FLAG_REQUEST_ACCESSIBILITY_BUTTON}.
+ * If the result is {@code true},
+ * then {@link AccessibilityManagerService#onUserStateChangedLocked(
+ * AccessibilityUserState, boolean)} should be called afterwards.
+ *
+ * @param packages list of packages that have stopped.
+ * @param userState user state to be read & modified.
+ * @return {@code true} if a service was enabled or a button target was removed,
+ * {@code false} otherwise.
+ */
+ @VisibleForTesting
+ boolean onPackagesForceStoppedLocked(
+ String[] packages, AccessibilityUserState userState) {
+ final List<String> continuousServicePackages =
+ userState.mInstalledServices.stream().filter(service ->
+ (service.flags & FLAG_REQUEST_ACCESSIBILITY_BUTTON)
+ == FLAG_REQUEST_ACCESSIBILITY_BUTTON
+ ).map(service -> service.getComponentName().flattenToString()).toList();
+
+ boolean enabledServicesChanged = false;
+ final Iterator<ComponentName> it = userState.mEnabledServices.iterator();
+ while (it.hasNext()) {
+ final ComponentName comp = it.next();
+ final String compPkg = comp.getPackageName();
+ for (String pkg : packages) {
+ if (compPkg.equals(pkg)) {
+ it.remove();
+ userState.getBindingServicesLocked().remove(comp);
+ userState.getCrashedServicesLocked().remove(comp);
+ enabledServicesChanged = true;
+ }
+ }
+ }
+ if (enabledServicesChanged) {
+ persistComponentNamesToSettingLocked(
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ userState.mEnabledServices, userState.mUserId);
+ }
+
+ boolean buttonTargetsChanged = userState.mAccessibilityButtonTargets.removeIf(
+ target -> continuousServicePackages.stream().anyMatch(
+ pkg -> Objects.equals(target, pkg)));
+ if (buttonTargetsChanged) {
+ persistColonDelimitedSetToSettingLocked(
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+ userState.mUserId,
+ userState.mAccessibilityButtonTargets, str -> str);
+ }
+
+ return enabledServicesChanged || buttonTargetsChanged;
+ }
+
@VisibleForTesting
PackageMonitor getPackageMonitor() {
return mPackageMonitor;
@@ -850,6 +918,16 @@
}
}
+ /**
+ * Handles instances in which a package or packages have forcibly stopped.
+ *
+ * @param intent intent containing package event information.
+ * @param uid linux process user id (different from Android user id).
+ * @param packages array of package names that have stopped.
+ * @param doit whether to try and handle the stop or just log the trace.
+ *
+ * @return {@code true} if package should be restarted, {@code false} otherwise.
+ */
@Override
public boolean onHandleForceStop(Intent intent, String[] packages,
int uid, boolean doit) {
@@ -867,26 +945,36 @@
return false;
}
final AccessibilityUserState userState = getUserStateLocked(userId);
- final Iterator<ComponentName> it = userState.mEnabledServices.iterator();
- while (it.hasNext()) {
- final ComponentName comp = it.next();
- final String compPkg = comp.getPackageName();
- for (String pkg : packages) {
- if (compPkg.equals(pkg)) {
- if (!doit) {
- return true;
+
+ if (Flags.disableContinuousShortcutOnForceStop()) {
+ if (doit && onPackagesForceStoppedLocked(packages, userState)) {
+ onUserStateChangedLocked(userState);
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ final Iterator<ComponentName> it = userState.mEnabledServices.iterator();
+ while (it.hasNext()) {
+ final ComponentName comp = it.next();
+ final String compPkg = comp.getPackageName();
+ for (String pkg : packages) {
+ if (compPkg.equals(pkg)) {
+ if (!doit) {
+ return true;
+ }
+ it.remove();
+ userState.getBindingServicesLocked().remove(comp);
+ userState.getCrashedServicesLocked().remove(comp);
+ persistComponentNamesToSettingLocked(
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ userState.mEnabledServices, userId);
+ onUserStateChangedLocked(userState);
}
- it.remove();
- userState.getBindingServicesLocked().remove(comp);
- userState.getCrashedServicesLocked().remove(comp);
- persistComponentNamesToSettingLocked(
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- userState.mEnabledServices, userId);
- onUserStateChangedLocked(userState);
}
}
+ return false;
}
- return false;
}
}
};
@@ -2452,7 +2540,8 @@
* @param userId The user id.
* @param outComponentNames The output component names.
*/
- private void readComponentNamesFromSettingLocked(String settingName, int userId,
+ @VisibleForTesting
+ void readComponentNamesFromSettingLocked(String settingName, int userId,
Set<ComponentName> outComponentNames) {
readColonDelimitedSettingToSet(settingName, userId,
str -> ComponentName.unflattenFromString(str), outComponentNames);
@@ -2481,7 +2570,19 @@
componentName -> componentName.flattenToShortString());
}
- private <T> void readColonDelimitedSettingToSet(String settingName, int userId,
+ /**
+ * Reads a colon delimited setting,
+ * passes the values through a function,
+ * then stores the values in a provided set.
+ *
+ * @param settingName Name of setting.
+ * @param userId user id corresponding to setting.
+ * @param toItem function mapping values to the output set.
+ * @param outSet output set to write to.
+ * @param <T> type of output set.
+ */
+ @VisibleForTesting
+ <T> void readColonDelimitedSettingToSet(String settingName, int userId,
Function<String, T> toItem, Set<T> outSet) {
final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
settingName, userId);
@@ -2693,8 +2794,9 @@
AccessibilityInputFilter inputFilter = null;
synchronized (mLock) {
int flags = 0;
- if (userState.isDisplayMagnificationEnabledLocked()) {
- flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER;
+ if (userState.isMagnificationSingleFingerTripleTapEnabledLocked()) {
+ flags |= AccessibilityInputFilter
+ .FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP;
}
if (userState.isShortcutMagnificationEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
@@ -3047,12 +3149,14 @@
}
private boolean readMagnificationEnabledSettingsLocked(AccessibilityUserState userState) {
- final boolean displayMagnificationEnabled = Settings.Secure.getIntForUser(
+ final boolean magnificationSingleFingerTripleTapEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
0, userState.mUserId) == 1;
- if ((displayMagnificationEnabled != userState.isDisplayMagnificationEnabledLocked())) {
- userState.setDisplayMagnificationEnabledLocked(displayMagnificationEnabled);
+ if ((magnificationSingleFingerTripleTapEnabled
+ != userState.isMagnificationSingleFingerTripleTapEnabledLocked())) {
+ userState.setMagnificationSingleFingerTripleTapEnabledLocked(
+ magnificationSingleFingerTripleTapEnabled);
return true;
}
return false;
@@ -3292,7 +3396,7 @@
// We would skip overlay display because it uses overlay window to simulate secondary
// displays in one display. It's not a real display and there's no input events for it.
final ArrayList<Display> displays = getValidDisplayList();
- if (userState.isDisplayMagnificationEnabledLocked()
+ if (userState.isMagnificationSingleFingerTripleTapEnabledLocked()
|| userState.isShortcutMagnificationEnabledLocked()) {
for (int i = 0; i < displays.size(); i++) {
final Display display = displays.get(i);
@@ -3321,7 +3425,7 @@
return;
}
final boolean connect = (userState.isShortcutMagnificationEnabledLocked()
- || userState.isDisplayMagnificationEnabledLocked())
+ || userState.isMagnificationSingleFingerTripleTapEnabledLocked())
&& (userState.getMagnificationCapabilitiesLocked()
!= Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)
|| userHasMagnificationServicesLocked(userState);
@@ -3472,7 +3576,7 @@
return true;
}
final boolean requestA11yButton = (serviceInfo.flags
- & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+ & FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
if (requestA11yButton && !userState.mEnabledServices.contains(componentName)) {
// An a11y service targeting sdk version > Q and request A11y button and is assigned
// to a11y btn should be in the enabled list.
@@ -3773,7 +3877,7 @@
final int targetSdk = installedServiceInfo.getResolveInfo()
.serviceInfo.applicationInfo.targetSdkVersion;
final boolean requestA11yButton = (installedServiceInfo.flags
- & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+ & FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
// Turns on / off the accessibility service
if ((targetSdk <= Build.VERSION_CODES.Q && shortcutType == ACCESSIBILITY_SHORTCUT_KEY)
|| (targetSdk > Build.VERSION_CODES.Q && !requestA11yButton)) {
@@ -5006,7 +5110,7 @@
updateWindowMagnificationConnectionIfNeeded(userState);
// Remove magnification button UI when the magnification capability is not all mode or
// magnification is disabled.
- if (!(userState.isDisplayMagnificationEnabledLocked()
+ if (!(userState.isMagnificationSingleFingerTripleTapEnabledLocked()
|| userState.isShortcutMagnificationEnabledLocked())
|| userState.getMagnificationCapabilitiesLocked()
!= Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 693526a..b4efec1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -109,7 +109,7 @@
private boolean mBindInstantServiceAllowed;
private boolean mIsAudioDescriptionByDefaultRequested;
private boolean mIsAutoclickEnabled;
- private boolean mIsDisplayMagnificationEnabled;
+ private boolean mIsMagnificationSingleFingerTripleTapEnabled;
private boolean mIsFilterKeyEventsEnabled;
private boolean mIsPerformGesturesEnabled;
private boolean mAccessibilityFocusOnlyInActiveWindow;
@@ -211,7 +211,7 @@
mRequestMultiFingerGestures = false;
mRequestTwoFingerPassthrough = false;
mSendMotionEventsEnabled = false;
- mIsDisplayMagnificationEnabled = false;
+ mIsMagnificationSingleFingerTripleTapEnabled = false;
mIsAutoclickEnabled = false;
mUserNonInteractiveUiTimeout = 0;
mUserInteractiveUiTimeout = 0;
@@ -520,7 +520,7 @@
.append(String.valueOf(mRequestTwoFingerPassthrough));
pw.append(", sendMotionEventsEnabled").append(String.valueOf(mSendMotionEventsEnabled));
pw.append(", displayMagnificationEnabled=").append(String.valueOf(
- mIsDisplayMagnificationEnabled));
+ mIsMagnificationSingleFingerTripleTapEnabled));
pw.append(", autoclickEnabled=").append(String.valueOf(mIsAutoclickEnabled));
pw.append(", nonInteractiveUiTimeout=").append(String.valueOf(mNonInteractiveUiTimeout));
pw.append(", interactiveUiTimeout=").append(String.valueOf(mInteractiveUiTimeout));
@@ -625,12 +625,12 @@
mIsAutoclickEnabled = enabled;
}
- public boolean isDisplayMagnificationEnabledLocked() {
- return mIsDisplayMagnificationEnabled;
+ public boolean isMagnificationSingleFingerTripleTapEnabledLocked() {
+ return mIsMagnificationSingleFingerTripleTapEnabled;
}
- public void setDisplayMagnificationEnabledLocked(boolean enabled) {
- mIsDisplayMagnificationEnabled = enabled;
+ public void setMagnificationSingleFingerTripleTapEnabledLocked(boolean enabled) {
+ mIsMagnificationSingleFingerTripleTapEnabled = enabled;
}
public boolean isFilterKeyEventsEnabledLocked() {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index e5225f6..898cdcc 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -96,11 +96,26 @@
out: ["com/android/server/location/contexthub/ContextHubStatsLog.java"],
}
+/*
+ * This module is used to refer aconfig flag libraries that are
+ * added to the framework via static_libs.
+ * These libraries are referred here via libs prevent duplication of classes in both
+ * the framework and the system server.
+*/
+java_defaults {
+ name: "shared-framework-aconfig-libs",
+ libs: [
+ "display_flags_lib",
+ "camera_platform_flags_core_java_lib",
+ ],
+}
+
java_library_static {
name: "services.core.unboosted",
defaults: [
"platform_service_defaults",
"android.hardware.power-java_static",
+ "shared-framework-aconfig-libs",
],
srcs: [
":android.hardware.biometrics.face-V3-java-source",
@@ -181,7 +196,6 @@
"android.hardware.power.stats-V2-java",
"android.hidl.manager-V1.2-java",
"cbor-java",
- "display_flags_lib",
"icu4j_calendar_astronomer",
"android.security.aaid_aidl-java",
"netd-client",
@@ -191,9 +205,8 @@
"ImmutabilityAnnotation",
"securebox",
"apache-commons-math",
- "power_optimization_flags_lib",
+ "backstage_power_flags_lib",
"notification_flags_lib",
- "camera_platform_flags_core_java_lib",
"biometrics_flags_lib",
"am_flags_lib",
],
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index a451f36..9bba08a 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2373,8 +2373,7 @@
}
}
- if (ppr.getLastProviderTime() > 0
- && (ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {
+ if ((ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {
if (adj > PREVIOUS_APP_ADJ) {
adj = PREVIOUS_APP_ADJ;
schedGroup = SCHED_GROUP_BACKGROUND;
diff --git a/services/core/java/com/android/server/am/ProcessProviderRecord.java b/services/core/java/com/android/server/am/ProcessProviderRecord.java
index 751e8a82..9b72a3a 100644
--- a/services/core/java/com/android/server/am/ProcessProviderRecord.java
+++ b/services/core/java/com/android/server/am/ProcessProviderRecord.java
@@ -34,7 +34,7 @@
/**
* The last time someone else was using a provider in this process.
*/
- private long mLastProviderTime;
+ private long mLastProviderTime = Long.MIN_VALUE;
/**
* class (String) -> ContentProviderRecord.
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index a9c388c..27c0876 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -280,7 +280,7 @@
* The last time the process was in the TOP state or greater.
*/
@GuardedBy("mService")
- private long mLastTopTime;
+ private long mLastTopTime = Long.MIN_VALUE;
/**
* Is this an empty background process?
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 2d231b3..aa788eb 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -118,6 +118,7 @@
// The list is sorted.
@VisibleForTesting
static final String[] sDeviceConfigAconfigScopes = new String[] {
+ "accessibility",
"android_core_networking",
"angle",
"arc_next",
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index 26d99d8..bb9ea28 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -2,7 +2,7 @@
flag {
name: "oomadjuster_correctness_rewrite"
- namespace: "android_platform_power_optimization"
+ namespace: "backstage_power"
description: "Utilize new OomAdjuster implementation"
bug: "298055811"
is_fixed_read_only: true
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 9cfac9a..eea3d38 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -68,6 +68,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
+import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -496,8 +497,9 @@
AudioDeviceInfo.TYPE_AUX_LINE
};
- /*package */ static boolean isValidCommunicationDevice(AudioDeviceInfo device) {
- return isValidCommunicationDeviceType(device.getType());
+ /*package */ static boolean isValidCommunicationDevice(@NonNull AudioDeviceInfo device) {
+ Objects.requireNonNull(device, "device must not be null");
+ return device.isSink() && isValidCommunicationDeviceType(device.getType());
}
private static boolean isValidCommunicationDeviceType(int deviceType) {
diff --git a/services/core/java/com/android/server/audio/MusicFxHelper.java b/services/core/java/com/android/server/audio/MusicFxHelper.java
index 6c0fef5..5f4e4c3 100644
--- a/services/core/java/com/android/server/audio/MusicFxHelper.java
+++ b/services/core/java/com/android/server/audio/MusicFxHelper.java
@@ -157,7 +157,8 @@
Log.w(TAG, " inside handle MSG_EFFECT_CLIENT_GONE");
// Once the uid is no longer running, close all remain audio session(s) for this UID
if (mClientUidSessionMap.get(Integer.valueOf(uid)) != null) {
- final List<Integer> sessions = mClientUidSessionMap.get(Integer.valueOf(uid));
+ final List<Integer> sessions =
+ new ArrayList(mClientUidSessionMap.get(Integer.valueOf(uid)));
Log.i(TAG, "UID " + uid + " gone, closing " + sessions.size() + " sessions");
for (Integer session : sessions) {
Intent intent = new Intent(
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 906c66d..76dde54 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -1474,11 +1474,11 @@
.getDrawable(R.drawable.ic_safety_protection);
toastToShow = Toast.makeCustomToastWithIcon(toastContext,
UiThread.get().getLooper(), message,
- Toast.LENGTH_SHORT, safetyProtectionIcon);
+ Toast.LENGTH_LONG, safetyProtectionIcon);
} else {
toastToShow = Toast.makeText(
toastContext, UiThread.get().getLooper(), message,
- Toast.LENGTH_SHORT);
+ Toast.LENGTH_LONG);
}
toastToShow.show();
}
diff --git a/services/core/java/com/android/server/display/DisplayBrightnessState.java b/services/core/java/com/android/server/display/DisplayBrightnessState.java
index 425a1af..6695801 100644
--- a/services/core/java/com/android/server/display/DisplayBrightnessState.java
+++ b/services/core/java/com/android/server/display/DisplayBrightnessState.java
@@ -27,6 +27,8 @@
* the DisplayBrightnessModeStrategies when updating the brightness.
*/
public final class DisplayBrightnessState {
+ public static final float CUSTOM_ANIMATION_RATE_NOT_SET = -1f;
+
private final float mBrightness;
private final float mSdrBrightness;
@@ -37,6 +39,8 @@
private final boolean mIsSlowChange;
+ private final float mCustomAnimationRate;
+
private DisplayBrightnessState(Builder builder) {
mBrightness = builder.getBrightness();
mSdrBrightness = builder.getSdrBrightness();
@@ -45,6 +49,7 @@
mShouldUseAutoBrightness = builder.getShouldUseAutoBrightness();
mIsSlowChange = builder.isSlowChange();
mMaxBrightness = builder.getMaxBrightness();
+ mCustomAnimationRate = builder.getCustomAnimationRate();
}
/**
@@ -97,7 +102,12 @@
return mMaxBrightness;
}
-
+ /**
+ * @return custom animation rate
+ */
+ public float getCustomAnimationRate() {
+ return mCustomAnimationRate;
+ }
@Override
public String toString() {
@@ -112,6 +122,7 @@
stringBuilder.append(getShouldUseAutoBrightness());
stringBuilder.append("\n isSlowChange:").append(mIsSlowChange);
stringBuilder.append("\n maxBrightness:").append(mMaxBrightness);
+ stringBuilder.append("\n customAnimationRate:").append(mCustomAnimationRate);
return stringBuilder.toString();
}
@@ -137,13 +148,14 @@
otherState.getDisplayBrightnessStrategyName())
&& mShouldUseAutoBrightness == otherState.getShouldUseAutoBrightness()
&& mIsSlowChange == otherState.isSlowChange()
- && mMaxBrightness == otherState.getMaxBrightness();
+ && mMaxBrightness == otherState.getMaxBrightness()
+ && mCustomAnimationRate == otherState.getCustomAnimationRate();
}
@Override
public int hashCode() {
return Objects.hash(mBrightness, mSdrBrightness, mBrightnessReason,
- mShouldUseAutoBrightness, mIsSlowChange, mMaxBrightness);
+ mShouldUseAutoBrightness, mIsSlowChange, mMaxBrightness, mCustomAnimationRate);
}
/**
@@ -164,6 +176,7 @@
private boolean mShouldUseAutoBrightness;
private boolean mIsSlowChange;
private float mMaxBrightness;
+ private float mCustomAnimationRate = CUSTOM_ANIMATION_RATE_NOT_SET;
/**
* Create a builder starting with the values from the specified {@link
@@ -180,6 +193,7 @@
builder.setShouldUseAutoBrightness(state.getShouldUseAutoBrightness());
builder.setIsSlowChange(state.isSlowChange());
builder.setMaxBrightness(state.getMaxBrightness());
+ builder.setCustomAnimationRate(state.getCustomAnimationRate());
return builder;
}
@@ -303,6 +317,22 @@
return mMaxBrightness;
}
+
+ /**
+ * See {@link DisplayBrightnessState#getCustomAnimationRate()}.
+ */
+ public Builder setCustomAnimationRate(float animationRate) {
+ this.mCustomAnimationRate = animationRate;
+ return this;
+ }
+
+ /**
+ * See {@link DisplayBrightnessState#getCustomAnimationRate()}.
+ */
+ public float getCustomAnimationRate() {
+ return mCustomAnimationRate;
+ }
+
/**
* This is used to construct an immutable DisplayBrightnessState object from its builder
*/
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 5b87eea..a788968 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -586,7 +586,8 @@
mSystemReady = false;
mConfigParameterProvider = new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
mExtraDisplayLoggingPackageName = DisplayProperties.debug_vri_package().orElse(null);
- mExtraDisplayEventLogging = !TextUtils.isEmpty(mExtraDisplayLoggingPackageName);
+ // TODO: b/306170135 - return TextUtils package name check instead
+ mExtraDisplayEventLogging = true;
}
public void setupSchedulerPolicies() {
@@ -2933,8 +2934,15 @@
// Send a display event if the display is enabled
private void sendDisplayEventIfEnabledLocked(@NonNull LogicalDisplay display,
@DisplayEvent int event) {
+ final boolean displayIsEnabled = display.isEnabledLocked();
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_POWER)) {
+ Trace.instant(Trace.TRACE_TAG_POWER,
+ "sendDisplayEventLocked#event=" + event + ",displayEnabled="
+ + displayIsEnabled);
+ }
+
// Only send updates outside of DisplayManagerService for enabled displays
- if (display.isEnabledLocked()) {
+ if (displayIsEnabled) {
sendDisplayEventLocked(display, event);
} else if (mExtraDisplayEventLogging) {
Slog.i(TAG, "Not Sending Display Event; display is not enabled: " + display);
@@ -2974,7 +2982,7 @@
// Check if the target app is in cached mode
private boolean isUidCached(int uid) {
- if (mActivityManagerInternal == null) {
+ if (mActivityManagerInternal == null || uid < FIRST_APPLICATION_UID) {
return false;
}
int procState = mActivityManagerInternal.getUidProcessState(uid);
@@ -2991,7 +2999,11 @@
+ displayId + ", event=" + event
+ (uids != null ? ", uids=" + uids : ""));
}
-
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_POWER)) {
+ Trace.instant(Trace.TRACE_TAG_POWER,
+ "deliverDisplayEvent#event=" + event + ",displayId="
+ + displayId + (uids != null ? ", uids=" + uids : ""));
+ }
// Grab the lock and copy the callbacks.
final int count;
synchronized (mSyncRoot) {
@@ -3031,7 +3043,8 @@
}
private boolean extraLogging(String packageName) {
- return mExtraDisplayEventLogging && mExtraDisplayLoggingPackageName.equals(packageName);
+ // TODO: b/306170135 - return mExtraDisplayLoggingPackageName & package name check instead
+ return true;
}
// Runs on Handler thread.
@@ -3498,10 +3511,13 @@
@Override
public void binderDied() {
- if (DEBUG || mExtraDisplayEventLogging && mExtraDisplayLoggingPackageName.equals(
- mPackageName)) {
+ if (DEBUG || extraLogging(mPackageName)) {
Slog.d(TAG, "Display listener for pid " + mPid + " died.");
}
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_POWER)) {
+ Trace.instant(Trace.TRACE_TAG_POWER,
+ "displayManagerBinderDied#mPid=" + mPid);
+ }
onCallbackDied(this);
}
@@ -3510,11 +3526,15 @@
*/
public boolean notifyDisplayEventAsync(int displayId, @DisplayEvent int event) {
if (!shouldSendEvent(event)) {
- if (mExtraDisplayEventLogging && mExtraDisplayLoggingPackageName.equals(
- mPackageName)) {
+ if (extraLogging(mPackageName)) {
Slog.i(TAG,
"Not sending displayEvent: " + event + " due to mask:" + mEventsMask);
}
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_POWER)) {
+ Trace.instant(Trace.TRACE_TAG_POWER,
+ "notifyDisplayEventAsync#notSendingEvent=" + event + ",mEventsMask="
+ + mEventsMask);
+ }
return true;
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 7d9c018..0f00027 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -561,8 +561,9 @@
brightnessSetting, () -> postBrightnessChangeRunnable(),
new HandlerExecutor(mHandler));
- mBrightnessClamperController = new BrightnessClamperController(mHandler,
- modeChangeCallback::run, new BrightnessClamperController.DisplayDeviceData(
+ mBrightnessClamperController = mInjector.getBrightnessClamperController(
+ mHandler, modeChangeCallback::run,
+ new BrightnessClamperController.DisplayDeviceData(
mUniqueDisplayId,
mThermalBrightnessThrottlingDataId,
logicalDisplay.getPowerThrottlingDataIdLocked(),
@@ -1353,6 +1354,8 @@
float rawBrightnessState = displayBrightnessState.getBrightness();
mBrightnessReasonTemp.set(displayBrightnessState.getBrightnessReason());
boolean slowChange = displayBrightnessState.isSlowChange();
+ // custom transition duration
+ float customAnimationRate = displayBrightnessState.getCustomAnimationRate();
// Set up the ScreenOff controller used when coming out of SCREEN_OFF and the ALS sensor
// doesn't yet have a valid lux value to use with auto-brightness.
@@ -1485,6 +1488,9 @@
brightnessState = clampedState.getBrightness();
slowChange = clampedState.isSlowChange();
+ // faster rate wins, at this point customAnimationRate == -1, strategy does not control
+ // customAnimationRate. Should be revisited if strategy start setting this value
+ customAnimationRate = Math.max(customAnimationRate, clampedState.getCustomAnimationRate());
mBrightnessReasonTemp.addModifier(clampedState.getBrightnessReason().getModifier());
if (updateScreenBrightnessSetting) {
@@ -1553,9 +1559,6 @@
// allowed range.
float animateValue = clampScreenBrightness(brightnessState);
- // custom transition duration
- float customTransitionRate = -1f;
-
// If there are any HDR layers on the screen, we have a special brightness value that we
// use instead. We still preserve the calculated brightness for Standard Dynamic Range
// (SDR) layers, but the main brightness value will be the one for HDR.
@@ -1570,10 +1573,21 @@
// We want to scale HDR brightness level with the SDR level, we also need to restore
// SDR brightness immediately when entering dim or low power mode.
animateValue = mBrightnessRangeController.getHdrBrightnessValue();
- customTransitionRate = mBrightnessRangeController.getHdrTransitionRate();
+ customAnimationRate = Math.max(customAnimationRate,
+ mBrightnessRangeController.getHdrTransitionRate());
mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_HDR);
}
+ // if doze or suspend state is requested, we want to finish brightnes animation fast
+ // to allow state animation to start
+ if (mPowerRequest.policy == DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE
+ && (mPowerRequest.dozeScreenState == Display.STATE_UNKNOWN // dozing
+ || mPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND
+ || mPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND)) {
+ customAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
+ slowChange = false;
+ }
+
final float currentBrightness = mPowerState.getScreenBrightness();
final float currentSdrBrightness = mPowerState.getSdrScreenBrightness();
@@ -1601,9 +1615,9 @@
if (skipAnimation) {
animateScreenBrightness(animateValue, sdrAnimateValue,
SCREEN_ANIMATION_RATE_MINIMUM);
- } else if (customTransitionRate > 0) {
+ } else if (customAnimationRate > 0) {
animateScreenBrightness(animateValue, sdrAnimateValue,
- customTransitionRate, /* ignoreAnimationLimits = */true);
+ customAnimationRate, /* ignoreAnimationLimits = */true);
} else {
boolean isIncreasing = animateValue > currentBrightness;
final float rampSpeed;
@@ -3059,6 +3073,15 @@
modeChangeCallback, displayDeviceConfig, handler, flags, displayToken, info);
}
+ BrightnessClamperController getBrightnessClamperController(Handler handler,
+ BrightnessClamperController.ClamperChangeListener clamperChangeListener,
+ BrightnessClamperController.DisplayDeviceData data, Context context,
+ DisplayManagerFlags flags) {
+
+ return new BrightnessClamperController(handler, clamperChangeListener, data, context,
+ flags);
+ }
+
DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
SensorManager sensorManager, Resources resources) {
return DisplayWhiteBalanceFactory.create(handler,
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
index 68f72d3..dfcda40 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
@@ -19,6 +19,8 @@
import android.annotation.NonNull;
import android.os.PowerManager;
+import com.android.server.display.DisplayBrightnessState;
+
import java.io.PrintWriter;
/**
@@ -33,6 +35,10 @@
return mBrightnessCap;
}
+ float getCustomAnimationRate() {
+ return DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
+ }
+
boolean isActive() {
return mIsActive;
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index 787f786..b574919 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -51,6 +51,7 @@
*/
public class BrightnessClamperController {
private static final String TAG = "BrightnessClamperController";
+
private final DeviceConfigParameterProvider mDeviceConfigParameterProvider;
private final Handler mHandler;
private final ClamperChangeListener mClamperChangeListenerExternal;
@@ -60,6 +61,8 @@
private final List<BrightnessModifier> mModifiers;
private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener;
private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
+
+ private float mCustomAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
@Nullable
private Type mClamperType = null;
private boolean mClamperApplied = false;
@@ -113,6 +116,7 @@
builder.setIsSlowChange(slowChange);
builder.setBrightness(cappedBrightness);
builder.setMaxBrightness(mBrightnessCap);
+ builder.setCustomAnimationRate(mCustomAnimationRate);
if (mClamperType != null) {
builder.getBrightnessReason().addModifier(BrightnessReason.MODIFIER_THROTTLED);
@@ -182,6 +186,7 @@
private void recalculateBrightnessCap() {
float brightnessCap = PowerManager.BRIGHTNESS_MAX;
Type clamperType = null;
+ float customAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
BrightnessClamper<?> minClamper = mClampers.stream()
.filter(BrightnessClamper::isActive)
@@ -191,11 +196,14 @@
if (minClamper != null) {
brightnessCap = minClamper.getBrightnessCap();
clamperType = minClamper.getType();
+ customAnimationRate = minClamper.getCustomAnimationRate();
}
- if (mBrightnessCap != brightnessCap || mClamperType != clamperType) {
+ if (mBrightnessCap != brightnessCap || mClamperType != clamperType
+ || mCustomAnimationRate != customAnimationRate) {
mBrightnessCap = brightnessCap;
mClamperType = clamperType;
+ mCustomAnimationRate = customAnimationRate;
mClamperChangeListenerExternal.onChanged();
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
index 39f0b13..200d88a 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
@@ -24,7 +24,6 @@
import android.view.SurfaceControlHdrLayerInfoListener;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.display.BrightnessUtils;
import com.android.server.display.config.HdrBrightnessData;
import java.io.PrintWriter;
@@ -176,21 +175,14 @@
} else if (mDesiredMaxBrightness != expectedMaxBrightness) {
mDesiredMaxBrightness = expectedMaxBrightness;
long debounceTime;
- long transitionDuration;
if (mDesiredMaxBrightness > mMaxBrightness) {
debounceTime = mHdrBrightnessData.mBrightnessIncreaseDebounceMillis;
- transitionDuration = mHdrBrightnessData.mBrightnessIncreaseDurationMillis;
+ mDesiredTransitionRate = mHdrBrightnessData.mScreenBrightnessRampIncrease;
} else {
debounceTime = mHdrBrightnessData.mBrightnessDecreaseDebounceMillis;
- transitionDuration = mHdrBrightnessData.mBrightnessDecreaseDurationMillis;
+ mDesiredTransitionRate = mHdrBrightnessData.mScreenBrightnessRampDecrease;
}
- float maxHlg = BrightnessUtils.convertLinearToGamma(mMaxBrightness);
- float desiredMaxHlg = BrightnessUtils.convertLinearToGamma(mDesiredMaxBrightness);
-
- mDesiredTransitionRate = Math.abs(
- (maxHlg - desiredMaxHlg) * 1000f / transitionDuration);
-
mHandler.removeCallbacks(mDebouncer);
mHandler.postDelayed(mDebouncer, debounceTime);
}
diff --git a/services/core/java/com/android/server/display/config/HdrBrightnessData.java b/services/core/java/com/android/server/display/config/HdrBrightnessData.java
index 48d671d..837fbf7 100644
--- a/services/core/java/com/android/server/display/config/HdrBrightnessData.java
+++ b/services/core/java/com/android/server/display/config/HdrBrightnessData.java
@@ -40,9 +40,9 @@
public final long mBrightnessIncreaseDebounceMillis;
/**
- * Brightness increase animation duration
+ * Brightness increase animation speed
*/
- public final long mBrightnessIncreaseDurationMillis;
+ public final float mScreenBrightnessRampIncrease;
/**
* Debounce time for brightness decrease
@@ -50,19 +50,19 @@
public final long mBrightnessDecreaseDebounceMillis;
/**
- * Brightness decrease animation duration
+ * Brightness decrease animation speed
*/
- public final long mBrightnessDecreaseDurationMillis;
+ public final float mScreenBrightnessRampDecrease;
@VisibleForTesting
public HdrBrightnessData(Map<Float, Float> maxBrightnessLimits,
- long brightnessIncreaseDebounceMillis, long brightnessIncreaseDurationMillis,
- long brightnessDecreaseDebounceMillis, long brightnessDecreaseDurationMillis) {
+ long brightnessIncreaseDebounceMillis, float screenBrightnessRampIncrease,
+ long brightnessDecreaseDebounceMillis, float screenBrightnessRampDecrease) {
mMaxBrightnessLimits = maxBrightnessLimits;
mBrightnessIncreaseDebounceMillis = brightnessIncreaseDebounceMillis;
- mBrightnessIncreaseDurationMillis = brightnessIncreaseDurationMillis;
+ mScreenBrightnessRampIncrease = screenBrightnessRampIncrease;
mBrightnessDecreaseDebounceMillis = brightnessDecreaseDebounceMillis;
- mBrightnessDecreaseDurationMillis = brightnessDecreaseDurationMillis;
+ mScreenBrightnessRampDecrease = screenBrightnessRampDecrease;
}
@Override
@@ -70,9 +70,9 @@
return "HdrBrightnessData {"
+ "mMaxBrightnessLimits: " + mMaxBrightnessLimits
+ ", mBrightnessIncreaseDebounceMillis: " + mBrightnessIncreaseDebounceMillis
- + ", mBrightnessIncreaseDurationMillis: " + mBrightnessIncreaseDurationMillis
+ + ", mScreenBrightnessRampIncrease: " + mScreenBrightnessRampIncrease
+ ", mBrightnessDecreaseDebounceMillis: " + mBrightnessDecreaseDebounceMillis
- + ", mBrightnessDecreaseDurationMillis: " + mBrightnessDecreaseDurationMillis
+ + ", mScreenBrightnessRampDecrease: " + mScreenBrightnessRampDecrease
+ "} ";
}
@@ -94,8 +94,8 @@
return new HdrBrightnessData(brightnessLimits,
hdrConfig.getBrightnessIncreaseDebounceMillis().longValue(),
- hdrConfig.getBrightnessIncreaseDurationMillis().longValue(),
+ hdrConfig.getScreenBrightnessRampIncrease().floatValue(),
hdrConfig.getBrightnessDecreaseDebounceMillis().longValue(),
- hdrConfig.getBrightnessDecreaseDurationMillis().longValue());
+ hdrConfig.getScreenBrightnessRampDecrease().floatValue());
}
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index f168f43..f35b045 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -240,6 +240,10 @@
private static final String LSKF_LAST_CHANGED_TIME_KEY = "sp-handle-ts";
private static final String USER_SERIAL_NUMBER_KEY = "serial-number";
+ private static final String MIGRATED_FRP2 = "migrated_frp2";
+ private static final String MIGRATED_KEYSTORE_NS = "migrated_keystore_namespace";
+ private static final String MIGRATED_SP_CE_ONLY = "migrated_all_users_to_sp_and_bound_ce";
+
// Duration that LockSettingsService will store the gatekeeper password for. This allows
// multiple biometric enrollments without prompting the user to enter their password via
// ConfirmLockPassword/ConfirmLockPattern multiple times. This needs to be at least the duration
@@ -909,14 +913,14 @@
}
private void migrateOldData() {
- if (getString("migrated_keystore_namespace", null, 0) == null) {
+ if (getString(MIGRATED_KEYSTORE_NS, null, 0) == null) {
boolean success = true;
synchronized (mSpManager) {
success &= mSpManager.migrateKeyNamespace();
}
success &= migrateProfileLockKeys();
if (success) {
- setString("migrated_keystore_namespace", "true", 0);
+ setString(MIGRATED_KEYSTORE_NS, "true", 0);
Slog.i(TAG, "Migrated keys to LSS namespace");
} else {
Slog.w(TAG, "Failed to migrate keys to LSS namespace");
@@ -936,9 +940,9 @@
// "migrated_frp" to "migrated_frp2" to cause migrateFrpCredential() to run again on devices
// where it had run before.
if (LockPatternUtils.frpCredentialEnabled(mContext)
- && !getBoolean("migrated_frp2", false, 0)) {
+ && !getBoolean(MIGRATED_FRP2, false, 0)) {
migrateFrpCredential();
- setBoolean("migrated_frp2", true, 0);
+ setBoolean(MIGRATED_FRP2, true, 0);
}
}
@@ -1028,14 +1032,14 @@
// If this gets interrupted (e.g. by the device powering off), there shouldn't be a
// problem since this will run again on the next boot, and setUserKeyProtection() is
// okay with the key being already protected by the given secret.
- if (getString("migrated_all_users_to_sp_and_bound_ce", null, 0) == null) {
+ if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null) {
for (UserInfo user : mUserManager.getAliveUsers()) {
removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
synchronized (mSpManager) {
migrateUserToSpWithBoundCeKeyLocked(user.id);
}
}
- setString("migrated_all_users_to_sp_and_bound_ce", "true", 0);
+ setString(MIGRATED_SP_CE_ONLY, "true", 0);
}
mThirdPartyAppsStarted = true;
@@ -1062,7 +1066,7 @@
Slogf.wtf(TAG, "Failed to unwrap synthetic password for unsecured user %d", userId);
return;
}
- setUserKeyProtection(userId, result.syntheticPassword.deriveFileBasedEncryptionKey());
+ setUserKeyProtection(userId, result.syntheticPassword);
}
}
@@ -1347,8 +1351,8 @@
AndroidKeyStoreMaintenance.onUserPasswordChanged(userHandle, password);
}
- private void unlockKeystore(byte[] password, int userHandle) {
- Authorization.onLockScreenEvent(false, userHandle, password, null);
+ private void unlockKeystore(int userId, SyntheticPassword sp) {
+ Authorization.onLockScreenEvent(false, userId, sp.deriveKeyStorePassword(), null);
}
@VisibleForTesting /** Note: this method is overridden in unit tests */
@@ -2001,7 +2005,8 @@
mStorage.writeChildProfileLock(profileUserId, ArrayUtils.concat(iv, ciphertext));
}
- private void setUserKeyProtection(@UserIdInt int userId, byte[] secret) {
+ private void setUserKeyProtection(@UserIdInt int userId, SyntheticPassword sp) {
+ final byte[] secret = sp.deriveFileBasedEncryptionKey();
final long callingId = Binder.clearCallingIdentity();
try {
mStorageManager.setUserKeyProtection(userId, secret);
@@ -2045,7 +2050,9 @@
}
}
- private void unlockUserKeyIfUnsecured(@UserIdInt int userId) {
+ @Override
+ public void unlockUserKeyIfUnsecured(@UserIdInt int userId) {
+ checkPasswordReadPermission();
synchronized (mSpManager) {
if (isUserKeyUnlocked(userId)) {
Slogf.d(TAG, "CE storage for user %d is already unlocked", userId);
@@ -2768,7 +2775,7 @@
final long protectorId = mSpManager.createLskfBasedProtector(getGateKeeperService(),
LockscreenCredential.createNone(), sp, userId);
setCurrentLskfBasedProtectorId(protectorId, userId);
- setUserKeyProtection(userId, sp.deriveFileBasedEncryptionKey());
+ setUserKeyProtection(userId, sp);
onSyntheticPasswordCreated(userId, sp);
Slogf.i(TAG, "Successfully initialized synthetic password for user %d", userId);
return sp;
@@ -2827,7 +2834,7 @@
}
}
- unlockKeystore(sp.deriveKeyStorePassword(), userId);
+ unlockKeystore(userId, sp);
unlockUserKey(userId, sp);
@@ -2894,7 +2901,7 @@
mSpManager.clearSidForUser(userId);
gateKeeperClearSecureUserId(userId);
unlockUserKey(userId, sp);
- unlockKeystore(sp.deriveKeyStorePassword(), userId);
+ unlockKeystore(userId, sp);
setKeystorePassword(null, userId);
removeBiometricsForUser(userId);
}
@@ -3454,11 +3461,6 @@
}
@Override
- public void unlockUserKeyIfUnsecured(@UserIdInt int userId) {
- LockSettingsService.this.unlockUserKeyIfUnsecured(userId);
- }
-
- @Override
public void createNewUser(@UserIdInt int userId, int userSerialNumber) {
LockSettingsService.this.createNewUser(userId, userSerialNumber);
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index df95c69..4bac872 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -174,7 +174,7 @@
pw.println(" Sets the lock screen as PIN, using the given PIN to unlock.");
pw.println("");
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(" Sets the lock screen as password, using the given PASSWORD to unlock.");
pw.println("");
pw.println(" clear [--old <CREDENTIAL>] [--user USER_ID]");
pw.println(" Clears the lock credentials.");
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index cc261a4..44719f8 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -515,8 +515,7 @@
// Binder call
@Override
- public RoutingSessionInfo getSystemSessionInfoForPackage(IMediaRouter2Manager manager,
- String packageName) {
+ public RoutingSessionInfo getSystemSessionInfoForPackage(@Nullable String packageName) {
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
boolean setDeviceRouteSelected = false;
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 0e8f907..2c59511 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -268,7 +268,14 @@
}
if (record.isSystemPriority()) {
if (DEBUG_KEY_EVENT) {
- Log.d(TAG, "Global priority session is updated, active=" + record.isActive());
+ Log.d(
+ TAG,
+ "Global priority session updated - user id="
+ + record.getUserId()
+ + " package="
+ + record.getPackageName()
+ + " active="
+ + record.isActive());
}
user.pushAddressedPlayerChangedLocked();
} else {
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionTimestampStore.java b/services/core/java/com/android/server/media/projection/MediaProjectionTimestampStore.java
new file mode 100644
index 0000000..4026d0c
--- /dev/null
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionTimestampStore.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media.projection;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Environment;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.InstantSource;
+
+/** Stores timestamps of media projection sessions. */
+public class MediaProjectionTimestampStore {
+ private static final String PREFERENCES_FILE_NAME = "media_projection_timestamp";
+ private static final String TIMESTAMP_PREF_KEY = "media_projection_timestamp_key";
+ private static final Object sInstanceLock = new Object();
+
+ @GuardedBy("sInstanceLock")
+ private static MediaProjectionTimestampStore sInstance;
+
+ private final Object mTimestampLock = new Object();
+
+ @GuardedBy("mTimestampLock")
+ private final SharedPreferences mSharedPreferences;
+
+ private final InstantSource mInstantSource;
+
+ @VisibleForTesting
+ public MediaProjectionTimestampStore(
+ SharedPreferences sharedPreferences, InstantSource instantSource) {
+ this.mSharedPreferences = sharedPreferences;
+ this.mInstantSource = instantSource;
+ }
+
+ /** Creates or returns an existing instance of {@link MediaProjectionTimestampStore}. */
+ public static MediaProjectionTimestampStore getInstance(Context context) {
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ File preferencesFile =
+ new File(Environment.getDataSystemDirectory(), PREFERENCES_FILE_NAME);
+ SharedPreferences preferences =
+ context.getSharedPreferences(preferencesFile, Context.MODE_PRIVATE);
+ sInstance = new MediaProjectionTimestampStore(preferences, InstantSource.system());
+ }
+ return sInstance;
+ }
+ }
+
+ /**
+ * Returns the time that has passed since the last active session, or {@code null} if there was
+ * no last active session.
+ */
+ @Nullable
+ public Duration timeSinceLastActiveSession() {
+ synchronized (mTimestampLock) {
+ Instant lastActiveSessionTimestamp = getLastActiveSessionTimestamp();
+ if (lastActiveSessionTimestamp == null) {
+ return null;
+ }
+ Instant now = mInstantSource.instant();
+ return Duration.between(lastActiveSessionTimestamp, now);
+ }
+ }
+
+ /** Registers that the current active session ended now. */
+ public void registerActiveSessionEnded() {
+ synchronized (mTimestampLock) {
+ Instant now = mInstantSource.instant();
+ setLastActiveSessionTimestamp(now);
+ }
+ }
+
+ @GuardedBy("mTimestampLock")
+ @Nullable
+ private Instant getLastActiveSessionTimestamp() {
+ long lastActiveSessionEpochMilli =
+ mSharedPreferences.getLong(TIMESTAMP_PREF_KEY, /* defValue= */ -1);
+ if (lastActiveSessionEpochMilli == -1) {
+ return null;
+ }
+ return Instant.ofEpochMilli(lastActiveSessionEpochMilli);
+ }
+
+ @GuardedBy("mTimestampLock")
+ private void setLastActiveSessionTimestamp(@NonNull Instant timestamp) {
+ mSharedPreferences.edit().putLong(TIMESTAMP_PREF_KEY, timestamp.toEpochMilli()).apply();
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 837b761..b4d36db 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3500,8 +3500,19 @@
null /* options */);
record = getToastRecord(callingUid, callingPid, pkg, isSystemToast, token,
text, callback, duration, windowToken, displayId, textCallback);
- mToastQueue.add(record);
- index = mToastQueue.size() - 1;
+
+ // Insert system toasts at the front of the queue
+ int systemToastInsertIdx = mToastQueue.size();
+ if (isSystemToast) {
+ systemToastInsertIdx = getInsertIndexForSystemToastLocked();
+ }
+ if (systemToastInsertIdx < mToastQueue.size()) {
+ index = systemToastInsertIdx;
+ mToastQueue.add(index, record);
+ } else {
+ mToastQueue.add(record);
+ index = mToastQueue.size() - 1;
+ }
keepProcessAliveForToastIfNeededLocked(callingPid);
}
// If it's at index 0, it's the current toast. It doesn't matter if it's
@@ -3517,6 +3528,23 @@
}
}
+ @GuardedBy("mToastQueue")
+ private int getInsertIndexForSystemToastLocked() {
+ // If there are other system toasts: insert after the last one
+ int idx = 0;
+ for (ToastRecord r : mToastQueue) {
+ if (idx == 0 && mIsCurrentToastShown) {
+ idx++;
+ continue;
+ }
+ if (!r.isSystemToast) {
+ return idx;
+ }
+ idx++;
+ }
+ return idx;
+ }
+
private boolean checkCanEnqueueToast(String pkg, int callingUid, int displayId,
boolean isAppRenderedToast, boolean isSystemToast) {
final boolean isPackageSuspended = isPackagePaused(pkg);
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 2967818..7556f27 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -113,6 +113,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ArchivedPackageParcel;
import android.content.pm.DataLoaderType;
+import android.content.pm.Flags;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
@@ -161,6 +162,7 @@
import com.android.server.EventLogTags;
import com.android.server.LocalManagerRegistry;
import com.android.server.SystemConfig;
+import com.android.server.art.model.ArtFlags;
import com.android.server.art.model.DexoptParams;
import com.android.server.art.model.DexoptResult;
import com.android.server.pm.Installer.LegacyDexoptDisabledException;
@@ -2562,8 +2564,15 @@
LocalManagerRegistry.getManager(PackageManagerLocal.class);
try (PackageManagerLocal.FilteredSnapshot snapshot =
packageManagerLocal.withFilteredSnapshot()) {
- DexoptParams params =
- dexoptOptions.convertToDexoptParams(0 /* extraFlags */);
+ boolean ignoreDexoptProfile =
+ (installRequest.getInstallFlags()
+ & PackageManager.INSTALL_IGNORE_DEXOPT_PROFILE)
+ != 0;
+ /*@DexoptFlags*/ int extraFlags =
+ ignoreDexoptProfile && Flags.useArtServiceV2()
+ ? ArtFlags.FLAG_IGNORE_PROFILE
+ : 0;
+ DexoptParams params = dexoptOptions.convertToDexoptParams(extraFlags);
DexoptResult dexOptResult = DexOptHelper.getArtManagerLocal().dexoptPackage(
snapshot, packageName, params);
installRequest.onDexoptFinished(dexOptResult);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d0e2f01..61b6b24 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -36,6 +36,7 @@
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
+import static android.util.FeatureFlagUtils.SETTINGS_TREAT_PAUSE_AS_QUARANTINE;
import static com.android.internal.annotations.VisibleForTesting.Visibility;
import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME;
@@ -164,6 +165,7 @@
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.ExceptionUtils;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -4564,6 +4566,7 @@
final Bundle extras = new Bundle();
extras.putInt(Intent.EXTRA_UID, pmi.getPackageUid(packageName, 0, userId));
extras.putInt(Intent.EXTRA_USER_HANDLE, userId);
+ extras.putLong(Intent.EXTRA_TIME, SystemClock.elapsedRealtime());
mHandler.post(() -> {
mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_UNSTOPPED,
packageName, extras,
@@ -6133,8 +6136,16 @@
final Computer snapshot = snapshotComputer();
enforceCanSetPackagesSuspendedAsUser(snapshot, callingPackage, callingUid, userId,
"setPackagesSuspendedAsUser");
- boolean quarantined = ((flags & PackageManager.FLAG_SUSPEND_QUARANTINED) != 0)
- && Flags.quarantinedEnabled();
+ boolean quarantined = false;
+ if (Flags.quarantinedEnabled()) {
+ if ((flags & PackageManager.FLAG_SUSPEND_QUARANTINED) != 0) {
+ quarantined = true;
+ } else if (FeatureFlagUtils.isEnabled(mContext,
+ SETTINGS_TREAT_PAUSE_AS_QUARANTINE)) {
+ final String wellbeingPkg = mContext.getString(R.string.config_systemWellbeing);
+ quarantined = callingPackage.equals(wellbeingPkg);
+ }
+ }
return mSuspendPackageHelper.setPackagesSuspended(snapshot, packageNames, suspended,
appExtras, launcherExtras, dialogInfo, callingPackage, userId, callingUid,
false /* forQuietMode */, quarantined);
@@ -6959,6 +6970,7 @@
final Bundle extras = new Bundle();
extras.putInt(Intent.EXTRA_UID, uid);
extras.putInt(Intent.EXTRA_USER_HANDLE, userId);
+ extras.putLong(Intent.EXTRA_TIME, SystemClock.elapsedRealtime());
mHandler.post(() -> {
mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_RESTARTED,
packageName, extras,
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 7264e2e..d4abad8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3703,6 +3703,9 @@
sessionParams.installFlags |=
PackageManager.INSTALL_BYPASS_LOW_TARGET_SDK_BLOCK;
break;
+ case "--ignore-dexopt-profile":
+ sessionParams.installFlags |= PackageManager.INSTALL_IGNORE_DEXOPT_PROFILE;
+ break;
default:
throw new IllegalArgumentException("Unknown option " + opt);
}
@@ -4799,7 +4802,7 @@
pw.println(" [--enable-rollback]");
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
pw.println(" [--apex] [--non-staged] [--force-non-staged]");
- pw.println(" [--staged-ready-timeout TIMEOUT]");
+ pw.println(" [--staged-ready-timeout TIMEOUT] [--ignore-dexopt-profile]");
pw.println(" [PATH [SPLIT...]|-]");
pw.println(" Install an application. Must provide the apk data to install, either as");
pw.println(" file path(s) or '-' to read from stdin. Options are:");
@@ -4839,6 +4842,13 @@
pw.println(" milliseconds for pre-reboot verification to complete when");
pw.println(" performing staged install. This flag is used to alter the waiting");
pw.println(" time. You can skip the waiting time by specifying a TIMEOUT of '0'");
+ pw.println(" --ignore-dexopt-profile: If set, all profiles are ignored by dexopt");
+ pw.println(" during the installation, including the profile in the DM file and");
+ pw.println(" the profile embedded in the APK file. If an invalid profile is");
+ pw.println(" provided during installation, no warning will be reported by `adb");
+ pw.println(" install`.");
+ pw.println(" This option does not affect later dexopt operations (e.g.,");
+ pw.println(" background dexopt and manual `pm compile` invocations).");
pw.println("");
pw.println(" install-existing [--user USER_ID|all|current]");
pw.println(" [--instant] [--full] [--wait] [--restrict-permissions] PACKAGE");
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 3ca933a..42f4cfb 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -238,34 +238,16 @@
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public PackageSetting(String name, String realName, @NonNull File path,
- String legacyNativeLibraryPath, String primaryCpuAbi,
- String secondaryCpuAbi, String cpuAbiOverride,
- long longVersionCode, int pkgFlags, int pkgPrivateFlags,
- int sharedUserAppId,
- String[] usesSdkLibraries, long[] usesSdkLibrariesVersionsMajor,
- String[] usesStaticLibraries, long[] usesStaticLibrariesVersions,
- Map<String, Set<String>> mimeGroups,
- @NonNull UUID domainSetId) {
+ public PackageSetting(@NonNull String name, @Nullable String realName, @NonNull File path,
+ int pkgFlags, int pkgPrivateFlags, @NonNull UUID domainSetId) {
super(pkgFlags, pkgPrivateFlags);
this.mName = name;
this.mRealName = realName;
- this.usesSdkLibraries = usesSdkLibraries;
- this.usesSdkLibrariesVersionsMajor = usesSdkLibrariesVersionsMajor;
- this.usesStaticLibraries = usesStaticLibraries;
- this.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
this.mPath = path;
this.mPathString = path.toString();
- this.legacyNativeLibraryPath = legacyNativeLibraryPath;
- this.mPrimaryCpuAbi = primaryCpuAbi;
- this.mSecondaryCpuAbi = secondaryCpuAbi;
- this.mCpuAbiOverride = cpuAbiOverride;
- this.versionCode = longVersionCode;
this.signatures = new PackageSignatures();
this.installSource = InstallSource.EMPTY;
- this.mSharedUserAppId = sharedUserAppId;
- mDomainSetId = domainSetId;
- copyMimeGroups(mimeGroups);
+ this.mDomainSetId = domainSetId;
mSnapshot = makeCache();
}
@@ -536,9 +518,10 @@
return this;
}
- public void setSharedUserAppId(int sharedUserAppId) {
+ public PackageSetting setSharedUserAppId(int sharedUserAppId) {
mSharedUserAppId = sharedUserAppId;
onChanged();
+ return this;
}
public PackageSetting setIsPersistent(boolean isPersistent) {
@@ -576,7 +559,7 @@
+ " " + mName + "/" + mAppId + "}";
}
- protected void copyMimeGroups(@Nullable Map<String, Set<String>> newMimeGroups) {
+ private void copyMimeGroups(@Nullable Map<String, Set<String>> newMimeGroups) {
if (newMimeGroups == null) {
mimeGroups = null;
return;
@@ -1250,7 +1233,8 @@
/**
* @see #mPath
*/
- PackageSetting setPath(@NonNull File path) {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public PackageSetting setPath(@NonNull File path) {
this.mPath = path;
this.mPathString = path.toString();
onChanged();
@@ -1451,9 +1435,11 @@
return this;
}
- public PackageSetting setMimeGroups(@NonNull Map<String, Set<String>> mimeGroups) {
- this.mimeGroups = mimeGroups;
- onChanged();
+ public PackageSetting setMimeGroups(@Nullable Map<String, Set<String>> mimeGroups) {
+ if (mimeGroups != null) {
+ copyMimeGroups(mimeGroups);
+ onChanged();
+ }
return this;
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index e726d91..a39178e 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -931,16 +931,24 @@
sharedUserSetting.mDisabledPackages.remove(p);
}
p.getPkgState().setUpdatedSystemApp(false);
- PackageSetting ret = addPackageLPw(name, p.getRealName(), p.getPath(),
- p.getLegacyNativeLibraryPath(), p.getPrimaryCpuAbiLegacy(),
- p.getSecondaryCpuAbiLegacy(), p.getCpuAbiOverride(),
- p.getAppId(), p.getVersionCode(), p.getFlags(), p.getPrivateFlags(),
- p.getUsesSdkLibraries(), p.getUsesSdkLibrariesVersionsMajor(),
- p.getUsesStaticLibraries(), p.getUsesStaticLibrariesVersions(), p.getMimeGroups(),
- mDomainVerificationManager.generateNewId());
+ PackageSetting ret = addPackageLPw(name, p.getRealName(), p.getPath(), p.getAppId(),
+ p.getFlags(), p.getPrivateFlags(), mDomainVerificationManager.generateNewId());
if (ret != null) {
+ ret.setLegacyNativeLibraryPath(p.getLegacyNativeLibraryPath());
+ ret.setPrimaryCpuAbi(p.getPrimaryCpuAbiLegacy());
+ ret.setSecondaryCpuAbi(p.getSecondaryCpuAbiLegacy());
+ ret.setCpuAbiOverride(p.getCpuAbiOverride());
+ ret.setLongVersionCode(p.getVersionCode());
+ ret.setUsesSdkLibraries(p.getUsesSdkLibraries());
+ ret.setUsesSdkLibrariesVersionsMajor(p.getUsesSdkLibrariesVersionsMajor());
+ ret.setUsesStaticLibraries(p.getUsesStaticLibraries());
+ ret.setUsesStaticLibrariesVersions(p.getUsesStaticLibrariesVersions());
+ ret.setMimeGroups(p.getMimeGroups());
ret.setAppMetadataFilePath(p.getAppMetadataFilePath());
ret.getPkgState().setUpdatedSystemApp(false);
+ ret.setIsPersistent(p.isPersistent());
+ ret.setTargetSdkVersion(p.getTargetSdkVersion());
+ ret.setRestrictUpdateHash(p.getRestrictUpdateHash());
}
mDisabledSysPackages.remove(name);
return ret;
@@ -961,13 +969,8 @@
}
}
- PackageSetting addPackageLPw(String name, String realName, File codePath,
- String legacyNativeLibraryPathString, String primaryCpuAbiString,
- String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc,
- int pkgFlags, int pkgPrivateFlags, String[] usesSdkLibraries,
- long[] usesSdkLibrariesVersions, String[] usesStaticLibraries,
- long[] usesStaticLibrariesVersions, Map<String, Set<String>> mimeGroups,
- @NonNull UUID domainSetId) {
+ PackageSetting addPackageLPw(String name, String realName, File codePath, int uid, int pkgFlags,
+ int pkgPrivateFlags, @NonNull UUID domainSetId) {
PackageSetting p = mPackages.get(name);
if (p != null) {
if (p.getAppId() == uid) {
@@ -977,11 +980,8 @@
"Adding duplicate package, keeping first: " + name);
return null;
}
- p = new PackageSetting(name, realName, codePath, legacyNativeLibraryPathString,
- primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, vc, pkgFlags,
- pkgPrivateFlags, 0 /*userId*/, usesSdkLibraries, usesSdkLibrariesVersions,
- usesStaticLibraries, usesStaticLibrariesVersions, mimeGroups, domainSetId);
- p.setAppId(uid);
+ p = new PackageSetting(name, realName, codePath, pkgFlags, pkgPrivateFlags, domainSetId)
+ .setAppId(uid);
if (mAppIds.registerExistingAppId(uid, p, name)) {
mPackages.put(name, p);
return p;
@@ -1092,16 +1092,21 @@
int installUserId = installUser != null ? installUser.getIdentifier()
: UserHandle.USER_SYSTEM;
- pkgSetting = new PackageSetting(pkgName, realPkgName, codePath,
- legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi,
- null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags,
- 0 /*sharedUserAppId*/, usesSdkLibraries, usesSdkLibrariesVersions,
- usesStaticLibraries, usesStaticLibrariesVersions,
- createMimeGroups(mimeGroupNames), domainSetId)
+ pkgSetting = new PackageSetting(pkgName, realPkgName, codePath, pkgFlags,
+ pkgPrivateFlags, domainSetId)
+ .setUsesSdkLibraries(usesSdkLibraries)
+ .setUsesSdkLibrariesVersionsMajor(usesSdkLibrariesVersions)
+ .setUsesStaticLibraries(usesStaticLibraries)
+ .setUsesStaticLibrariesVersions(usesStaticLibrariesVersions)
+ .setLegacyNativeLibraryPath(legacyNativeLibraryPath)
+ .setPrimaryCpuAbi(primaryCpuAbi)
+ .setSecondaryCpuAbi(secondaryCpuAbi)
+ .setLongVersionCode(versionCode)
+ .setMimeGroups(createMimeGroups(mimeGroupNames))
.setIsPersistent(isPersistent)
.setTargetSdkVersion(targetSdkVersion)
- .setRestrictUpdateHash(restrictUpdatedHash);
- pkgSetting.setLastModifiedTime(codePath.lastModified());
+ .setRestrictUpdateHash(restrictUpdatedHash)
+ .setLastModifiedTime(codePath.lastModified());
if (sharedUser != null) {
pkgSetting.setSharedUserAppId(sharedUser.mAppId);
}
@@ -3066,6 +3071,12 @@
serializer.attributeLongHex(null, "ft", pkg.getLastModifiedTime());
serializer.attributeLongHex(null, "ut", pkg.getLastUpdateTime());
serializer.attributeLong(null, "version", pkg.getVersionCode());
+ serializer.attributeBoolean(null, "isPersistent", pkg.isPersistent());
+ serializer.attributeInt(null, "targetSdkVersion", pkg.getTargetSdkVersion());
+ if (pkg.getRestrictUpdateHash() != null) {
+ serializer.attributeBytesBase64(null, "restrictUpdateHash",
+ pkg.getRestrictUpdateHash());
+ }
if (pkg.getLegacyNativeLibraryPath() != null) {
serializer.attribute(null, "nativeLibraryPath", pkg.getLegacyNativeLibraryPath());
}
@@ -3129,6 +3140,12 @@
serializer.attributeLongHex(null, "ft", pkg.getLastModifiedTime());
serializer.attributeLongHex(null, "ut", pkg.getLastUpdateTime());
serializer.attributeLong(null, "version", pkg.getVersionCode());
+ serializer.attributeBoolean(null, "isPersistent", pkg.isPersistent());
+ serializer.attributeInt(null, "targetSdkVersion", pkg.getTargetSdkVersion());
+ if (pkg.getRestrictUpdateHash() != null) {
+ serializer.attributeBytesBase64(null, "restrictUpdateHash",
+ pkg.getRestrictUpdateHash());
+ }
if (!pkg.hasSharedUser()) {
serializer.attributeInt(null, "userId", pkg.getAppId());
} else {
@@ -3861,6 +3878,10 @@
}
long versionCode = parser.getAttributeLong(null, "version", 0);
+ boolean isPersistent = parser.getAttributeBoolean(null, "isPersistent", false);
+ int targetSdkVersion = parser.getAttributeInt(null, "targetSdkVersion", 0);
+ byte[] restrictUpdateHash = parser.getAttributeBytesBase64(null, "restrictUpdateHash",
+ null);
int pkgFlags = 0;
int pkgPrivateFlags = 0;
@@ -3873,10 +3894,16 @@
// debug invalid entries. The actual logic for migrating to a new ID is done in other
// methods that use DomainVerificationManagerInternal#generateNewId
UUID domainSetId = DomainVerificationManagerInternal.DISABLED_ID;
- PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr),
- legacyNativeLibraryPathStr, primaryCpuAbiStr, secondaryCpuAbiStr, cpuAbiOverrideStr,
- versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserAppId*/, null, null, null,
- null, null, domainSetId);
+ PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr), pkgFlags,
+ pkgPrivateFlags, domainSetId)
+ .setLegacyNativeLibraryPath(legacyNativeLibraryPathStr)
+ .setPrimaryCpuAbi(primaryCpuAbiStr)
+ .setSecondaryCpuAbi(secondaryCpuAbiStr)
+ .setCpuAbiOverride(cpuAbiOverrideStr)
+ .setLongVersionCode(versionCode)
+ .setIsPersistent(isPersistent)
+ .setTargetSdkVersion(targetSdkVersion)
+ .setRestrictUpdateHash(restrictUpdateHash);
long timeStamp = parser.getAttributeLongHex(null, "ft", 0);
if (timeStamp == 0) {
timeStamp = parser.getAttributeLong(null, "ts", 0);
@@ -3970,6 +3997,9 @@
long loadingCompletedTime = 0;
UUID domainSetId;
String appMetadataFilePath = null;
+ boolean isPersistent = false;
+ int targetSdkVersion = 0;
+ byte[] restrictUpdateHash = null;
try {
name = parser.getAttributeValue(null, ATTR_NAME);
realName = parser.getAttributeValue(null, "realName");
@@ -3993,6 +4023,9 @@
}
versionCode = parser.getAttributeLong(null, "version", 0);
+ isPersistent = parser.getAttributeBoolean(null, "isPersistent", false);
+ targetSdkVersion = parser.getAttributeInt(null, "targetSdkVersion", 0);
+ restrictUpdateHash = parser.getAttributeBytesBase64(null, "restrictUpdateHash", null);
installerPackageName = parser.getAttributeValue(null, "installer");
installerPackageUid = parser.getAttributeInt(null, "installerUid", INVALID_UID);
updateOwnerPackageName = parser.getAttributeValue(null, "updateOwner");
@@ -4088,11 +4121,7 @@
+ parser.getPositionDescription());
} else if (appId > 0) {
packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
- legacyNativeLibraryPathStr, primaryCpuAbiString, secondaryCpuAbiString,
- cpuAbiOverrideString, appId, versionCode, pkgFlags, pkgPrivateFlags,
- null /* usesSdkLibraries */, null /* usesSdkLibraryVersions */,
- null /* usesStaticLibraries */, null /* usesStaticLibraryVersions */,
- null /* mimeGroups */, domainSetId);
+ appId, pkgFlags, pkgPrivateFlags, domainSetId);
if (PackageManagerService.DEBUG_SETTINGS)
Log.i(PackageManagerService.TAG, "Reading package " + name + ": appId="
+ appId + " pkg=" + packageSetting);
@@ -4101,22 +4130,26 @@
+ appId + " while parsing settings at "
+ parser.getPositionDescription());
} else {
+ packageSetting.setLegacyNativeLibraryPath(legacyNativeLibraryPathStr);
+ packageSetting.setPrimaryCpuAbi(primaryCpuAbiString);
+ packageSetting.setSecondaryCpuAbi(secondaryCpuAbiString);
+ packageSetting.setCpuAbiOverride(cpuAbiOverrideString);
+ packageSetting.setLongVersionCode(versionCode);
packageSetting.setLastModifiedTime(timeStamp);
packageSetting.setLastUpdateTime(lastUpdateTime);
}
} else if (sharedUserAppId != 0) {
if (sharedUserAppId > 0) {
packageSetting = new PackageSetting(name.intern(), realName,
- new File(codePathStr), legacyNativeLibraryPathStr,
- primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
- versionCode, pkgFlags, pkgPrivateFlags, sharedUserAppId,
- null /* usesSdkLibraries */,
- null /* usesSdkLibrariesVersions */,
- null /* usesStaticLibraries */,
- null /* usesStaticLibraryVersions */,
- null /* mimeGroups */, domainSetId);
- packageSetting.setLastModifiedTime(timeStamp);
- packageSetting.setLastUpdateTime(lastUpdateTime);
+ new File(codePathStr), pkgFlags, pkgPrivateFlags, domainSetId)
+ .setLegacyNativeLibraryPath(legacyNativeLibraryPathStr)
+ .setPrimaryCpuAbi(primaryCpuAbiString)
+ .setSecondaryCpuAbi(secondaryCpuAbiString)
+ .setCpuAbiOverride(cpuAbiOverrideString)
+ .setLongVersionCode(versionCode)
+ .setSharedUserAppId(sharedUserAppId)
+ .setLastModifiedTime(timeStamp)
+ .setLastUpdateTime(lastUpdateTime);
mPendingPackages.add(packageSetting);
if (PackageManagerService.DEBUG_SETTINGS)
Log.i(PackageManagerService.TAG, "Reading package " + name
@@ -4155,7 +4188,10 @@
.setForceQueryableOverride(installedForceQueryable)
.setLoadingProgress(loadingProgress)
.setLoadingCompletedTime(loadingCompletedTime)
- .setAppMetadataFilePath(appMetadataFilePath);
+ .setAppMetadataFilePath(appMetadataFilePath)
+ .setIsPersistent(isPersistent)
+ .setTargetSdkVersion(targetSdkVersion)
+ .setRestrictUpdateHash(restrictUpdateHash);
// Handle legacy string here for single-user mode
final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
if (enabledStr != null) {
@@ -4916,9 +4952,11 @@
}
pw.print(prefix); pw.print(" versionCode="); pw.print(ps.getVersionCode());
if (pkg != null) {
- pw.print(" minSdk="); pw.print(pkg.getMinSdkVersion());
- pw.print(" targetSdk="); pw.println(pkg.getTargetSdkVersion());
-
+ pw.print(" minSdk=");
+ pw.print(pkg.getMinSdkVersion());
+ }
+ pw.print(" targetSdk="); pw.println(ps.getTargetSdkVersion());
+ if (pkg != null) {
SparseIntArray minExtensionVersions = pkg.getMinExtensionVersions();
pw.print(prefix); pw.print(" minExtensionVersions=[");
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index 9987867..585e2e4 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import static android.content.pm.Flags.sdkLibIndependence;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_LIBRARY_BAD_CERTIFICATE_DIGEST;
@@ -951,10 +952,12 @@
}
}
if (!pkg.getUsesSdkLibraries().isEmpty()) {
+ // Allow installation even if sdk-library dependency doesn't exist
+ boolean required = !sdkLibIndependence();
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesSdkLibraries(),
pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesCertDigests(),
- pkg.getPackageName(), "sdk", true, pkg.getTargetSdkVersion(), usesLibraryInfos,
- availablePackages, newLibraries);
+ pkg.getPackageName(), "sdk", required, pkg.getTargetSdkVersion(),
+ usesLibraryInfos, availablePackages, newLibraries);
}
return usesLibraryInfos;
}
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index d6e35e8..a33e353 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -347,8 +347,15 @@
UserHandle user = UserHandle.getUserHandleForUid(uid);
PermissionControllerManager manager = mPermControllerManagers.get(user);
if (manager == null) {
- manager = new PermissionControllerManager(
- getUserContext(getContext(), user), PermissionThread.getHandler());
+ try {
+ manager = new PermissionControllerManager(
+ getUserContext(getContext(), user), PermissionThread.getHandler());
+ } catch (IllegalArgumentException exception) {
+ // There's a possible race condition when a user is being removed
+ Log.e(LOG_TAG, "Could not create PermissionControllerManager for user"
+ + user, exception);
+ return;
+ }
mPermControllerManagers.put(user, manager);
}
manager.updateUserSensitiveForApp(uid);
diff --git a/services/core/java/com/android/server/power/Android.bp b/services/core/java/com/android/server/power/Android.bp
index 1da9dd7..607d435 100644
--- a/services/core/java/com/android/server/power/Android.bp
+++ b/services/core/java/com/android/server/power/Android.bp
@@ -1,5 +1,5 @@
aconfig_declarations {
- name: "power_optimization_flags",
+ name: "backstage_power_flags",
package: "com.android.server.power.optimization",
srcs: [
"stats/*.aconfig",
@@ -7,6 +7,6 @@
}
java_aconfig_library {
- name: "power_optimization_flags_lib",
- aconfig_declarations: "power_optimization_flags",
+ name: "backstage_power_flags_lib",
+ aconfig_declarations: "backstage_power_flags",
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index dfbcbae6..4a4214f 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3347,8 +3347,6 @@
} else {
startDreaming = false;
}
- Slog.i(TAG, "handleSandman powerGroup=" + groupId + " startDreaming=" + startDreaming
- + " wakefulness=" + wakefulnessToString(wakefulness));
}
// Start dreaming if needed.
@@ -3383,23 +3381,19 @@
if (startDreaming && isDreaming) {
mDreamsBatteryLevelDrain = 0;
if (wakefulness == WAKEFULNESS_DOZING) {
- Slog.i(TAG, "Dozing powerGroup " + groupId);
+ Slog.i(TAG, "Dozing...");
} else {
- Slog.i(TAG, "Dreaming powerGroup " + groupId);
+ Slog.i(TAG, "Dreaming...");
}
}
// If preconditions changed, wait for the next iteration to determine
// whether the dream should continue (or be restarted).
final PowerGroup powerGroup = mPowerGroups.get(groupId);
- final int newWakefulness = powerGroup.getWakefulnessLocked();
if (powerGroup.isSandmanSummonedLocked()
- || newWakefulness != wakefulness) {
+ || powerGroup.getWakefulnessLocked() != wakefulness) {
return; // wait for next cycle
}
- Slog.i(TAG, "handleSandman powerGroup=" + groupId + " isDreaming=" + isDreaming
- + " wakefulness=" + newWakefulness);
-
// Determine whether the dream should continue.
long now = mClock.uptimeMillis();
diff --git a/services/core/java/com/android/server/power/stats/flags.aconfig b/services/core/java/com/android/server/power/stats/flags.aconfig
index add806f..0f13571 100644
--- a/services/core/java/com/android/server/power/stats/flags.aconfig
+++ b/services/core/java/com/android/server/power/stats/flags.aconfig
@@ -2,14 +2,14 @@
flag {
name: "power_monitor_api"
- namespace: "power_optimization"
+ namespace: "backstage_power"
description: "Feature flag for ODPM API"
bug: "295027807"
}
flag {
name: "streamlined_battery_stats"
- namespace: "power_optimization"
+ namespace: "backstage_power"
description: "Feature flag for streamlined battery stats"
bug: "285646152"
}
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
index becbbf2..519acec 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
@@ -22,11 +22,10 @@
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.VibratorInfo;
+import android.os.vibrator.Flags;
import android.util.Slog;
import android.util.SparseArray;
import android.view.HapticFeedbackConstants;
-import android.view.flags.FeatureFlags;
-import android.view.flags.FeatureFlagsImpl;
import com.android.internal.annotations.VisibleForTesting;
@@ -56,7 +55,8 @@
// If present and valid, a vibration here will be used for an effect.
// Otherwise, the system's default vibration will be used.
@Nullable private final SparseArray<VibrationEffect> mHapticCustomizations;
- private final FeatureFlags mViewFeatureFlags;
+
+ private float mKeyboardVibrationFixedAmplitude;
/** @hide */
public HapticFeedbackVibrationProvider(Resources res, Vibrator vibrator) {
@@ -65,16 +65,14 @@
/** @hide */
public HapticFeedbackVibrationProvider(Resources res, VibratorInfo vibratorInfo) {
- this(res, vibratorInfo, loadHapticCustomizations(res, vibratorInfo),
- new FeatureFlagsImpl());
+ this(res, vibratorInfo, loadHapticCustomizations(res, vibratorInfo));
}
/** @hide */
@VisibleForTesting HapticFeedbackVibrationProvider(
Resources res,
VibratorInfo vibratorInfo,
- @Nullable SparseArray<VibrationEffect> hapticCustomizations,
- FeatureFlags viewFeatureFlags) {
+ @Nullable SparseArray<VibrationEffect> hapticCustomizations) {
mVibratorInfo = vibratorInfo;
mHapticTextHandleEnabled = res.getBoolean(
com.android.internal.R.bool.config_enableHapticTextHandle);
@@ -83,14 +81,17 @@
hapticCustomizations = null;
}
mHapticCustomizations = hapticCustomizations;
- mViewFeatureFlags = viewFeatureFlags;
-
mSafeModeEnabledVibrationEffect =
effectHasCustomization(HapticFeedbackConstants.SAFE_MODE_ENABLED)
? mHapticCustomizations.get(HapticFeedbackConstants.SAFE_MODE_ENABLED)
: VibrationSettings.createEffectFromResource(
res,
com.android.internal.R.array.config_safeModeEnabledVibePattern);
+ mKeyboardVibrationFixedAmplitude = res.getFloat(
+ com.android.internal.R.dimen.config_keyboardHapticFeedbackFixedAmplitude);
+ if (mKeyboardVibrationFixedAmplitude < 0 || mKeyboardVibrationFixedAmplitude > 1) {
+ mKeyboardVibrationFixedAmplitude = -1;
+ }
}
/**
@@ -120,6 +121,9 @@
return getVibration(effectId, VibrationEffect.EFFECT_TEXTURE_TICK);
case HapticFeedbackConstants.KEYBOARD_RELEASE:
+ case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS
+ return getKeyboardVibration(effectId);
+
case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
case HapticFeedbackConstants.ENTRY_BUMP:
case HapticFeedbackConstants.DRAG_CROSSING:
@@ -128,7 +132,6 @@
VibrationEffect.EFFECT_TICK,
/* fallbackForPredefinedEffect= */ false);
- case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS
case HapticFeedbackConstants.VIRTUAL_KEY:
case HapticFeedbackConstants.EDGE_RELEASE:
case HapticFeedbackConstants.CALENDAR_DATE:
@@ -204,6 +207,10 @@
case HapticFeedbackConstants.SCROLL_LIMIT:
attrs = HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES;
break;
+ case HapticFeedbackConstants.KEYBOARD_TAP:
+ case HapticFeedbackConstants.KEYBOARD_RELEASE:
+ attrs = createKeyboardVibrationAttributes();
+ break;
default:
attrs = TOUCH_VIBRATION_ATTRIBUTES;
}
@@ -212,9 +219,12 @@
if (bypassVibrationIntensitySetting) {
flags |= VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
}
- if (shouldBypassInterruptionPolicy(effectId, mViewFeatureFlags)) {
+ if (shouldBypassInterruptionPolicy(effectId)) {
flags |= VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
}
+ if (shouldBypassIntensityScale(effectId)) {
+ flags |= VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE;
+ }
return flags == 0 ? attrs : new VibrationAttributes.Builder(attrs).setFlags(flags).build();
}
@@ -295,6 +305,64 @@
return mHapticCustomizations != null && mHapticCustomizations.contains(effectId);
}
+ private VibrationEffect getKeyboardVibration(int effectId) {
+ if (effectHasCustomization(effectId)) {
+ return mHapticCustomizations.get(effectId);
+ }
+
+ int primitiveId;
+ int predefinedEffectId;
+ boolean predefinedEffectFallback;
+
+ switch (effectId) {
+ case HapticFeedbackConstants.KEYBOARD_RELEASE:
+ primitiveId = VibrationEffect.Composition.PRIMITIVE_TICK;
+ predefinedEffectId = VibrationEffect.EFFECT_TICK;
+ predefinedEffectFallback = false;
+ break;
+ case HapticFeedbackConstants.KEYBOARD_TAP:
+ default:
+ primitiveId = VibrationEffect.Composition.PRIMITIVE_CLICK;
+ predefinedEffectId = VibrationEffect.EFFECT_CLICK;
+ predefinedEffectFallback = true;
+ }
+ if (Flags.keyboardCategoryEnabled() && mKeyboardVibrationFixedAmplitude > 0) {
+ if (mVibratorInfo.isPrimitiveSupported(primitiveId)) {
+ return VibrationEffect.startComposition()
+ .addPrimitive(primitiveId, mKeyboardVibrationFixedAmplitude)
+ .compose();
+ }
+ }
+ return getVibration(effectId, predefinedEffectId,
+ /* fallbackForPredefinedEffect= */ predefinedEffectFallback);
+ }
+
+ private boolean shouldBypassIntensityScale(int effectId) {
+ if (!Flags.keyboardCategoryEnabled() || mKeyboardVibrationFixedAmplitude < 0) {
+ // shouldn't bypass if not support keyboard category or no fixed amplitude
+ return false;
+ }
+ switch (effectId) {
+ case HapticFeedbackConstants.KEYBOARD_TAP:
+ return mVibratorInfo.isPrimitiveSupported(
+ VibrationEffect.Composition.PRIMITIVE_CLICK);
+ case HapticFeedbackConstants.KEYBOARD_RELEASE:
+ return mVibratorInfo.isPrimitiveSupported(
+ VibrationEffect.Composition.PRIMITIVE_TICK);
+ }
+ return false;
+ }
+
+ private static VibrationAttributes createKeyboardVibrationAttributes() {
+ if (!Flags.keyboardCategoryEnabled()) {
+ return TOUCH_VIBRATION_ATTRIBUTES;
+ }
+
+ return new VibrationAttributes.Builder(TOUCH_VIBRATION_ATTRIBUTES)
+ .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
+ .build();
+ }
+
@Nullable
private static SparseArray<VibrationEffect> loadHapticCustomizations(
Resources res, VibratorInfo vibratorInfo) {
@@ -306,8 +374,7 @@
}
}
- private static boolean shouldBypassInterruptionPolicy(
- int effectId, FeatureFlags viewFeatureFlags) {
+ private static boolean shouldBypassInterruptionPolicy(int effectId) {
switch (effectId) {
case HapticFeedbackConstants.SCROLL_TICK:
case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
@@ -315,7 +382,7 @@
// The SCROLL_* constants should bypass interruption filter, so that scroll haptics
// can play regardless of focus modes like DND. Guard this behavior by the feature
// flag controlling the general scroll feedback APIs.
- return viewFeatureFlags.scrollFeedbackApi();
+ return android.view.flags.Flags.scrollFeedbackApi();
default:
return false;
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index db8a9ae..7f55836 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -16,6 +16,7 @@
package com.android.server.vibrator;
+import static android.os.VibrationAttributes.CATEGORY_KEYBOARD;
import static android.os.VibrationAttributes.USAGE_ACCESSIBILITY;
import static android.os.VibrationAttributes.USAGE_ALARM;
import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
@@ -52,6 +53,7 @@
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.Vibrator.VibrationIntensity;
+import android.os.vibrator.Flags;
import android.os.vibrator.VibrationConfig;
import android.provider.Settings;
import android.util.IndentingPrintWriter;
@@ -188,6 +190,8 @@
@GuardedBy("mLock")
private boolean mVibrateOn;
@GuardedBy("mLock")
+ private boolean mKeyboardVibrationOn;
+ @GuardedBy("mLock")
private int mRingerMode;
@GuardedBy("mLock")
private boolean mOnWirelessCharger;
@@ -295,6 +299,8 @@
Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY));
registerSettingsObserver(
Settings.System.getUriFor(Settings.System.RING_VIBRATION_INTENSITY));
+ registerSettingsObserver(
+ Settings.System.getUriFor(Settings.System.KEYBOARD_VIBRATION_ENABLED));
if (mVibrationConfig.ignoreVibrationsOnWirelessCharger()) {
Intent batteryStatus = mContext.registerReceiver(
@@ -418,14 +424,9 @@
}
if (!callerInfo.attrs.isFlagSet(
- VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)) {
- if (!mVibrateOn && (VIBRATE_ON_DISABLED_USAGE_ALLOWED != usage)) {
- return Vibration.Status.IGNORED_FOR_SETTINGS;
- }
-
- if (getCurrentIntensity(usage) == Vibrator.VIBRATION_INTENSITY_OFF) {
- return Vibration.Status.IGNORED_FOR_SETTINGS;
- }
+ VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
+ && !shouldVibrateForUserSetting(callerInfo)) {
+ return Vibration.Status.IGNORED_FOR_SETTINGS;
}
if (!callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)) {
@@ -497,6 +498,30 @@
return mRingerMode != AudioManager.RINGER_MODE_SILENT;
}
+ /**
+ * Return {@code true} if the device should vibrate for user setting, and
+ * {@code false} to ignore the vibration.
+ */
+ @GuardedBy("mLock")
+ private boolean shouldVibrateForUserSetting(Vibration.CallerInfo callerInfo) {
+ final int usage = callerInfo.attrs.getUsage();
+ if (!mVibrateOn && (VIBRATE_ON_DISABLED_USAGE_ALLOWED != usage)) {
+ // Main setting disabled.
+ return false;
+ }
+
+ if (Flags.keyboardCategoryEnabled()) {
+ int category = callerInfo.attrs.getCategory();
+ if (usage == USAGE_TOUCH && category == CATEGORY_KEYBOARD) {
+ // Keyboard touch has a different user setting.
+ return mKeyboardVibrationOn;
+ }
+ }
+
+ // Apply individual user setting based on usage.
+ return getCurrentIntensity(usage) != Vibrator.VIBRATION_INTENSITY_OFF;
+ }
+
/** Update all cached settings and triggers registered listeners. */
void update() {
updateSettings();
@@ -508,6 +533,8 @@
synchronized (mLock) {
mVibrateInputDevices = loadSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0;
mVibrateOn = loadSystemSetting(Settings.System.VIBRATE_ON, 1) > 0;
+ mKeyboardVibrationOn = loadSystemSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED,
+ mVibrationConfig.isDefaultKeyboardVibrationEnabled() ? 1 : 0) > 0;
int alarmIntensity = toIntensity(
loadSystemSetting(Settings.System.ALARM_VIBRATION_INTENSITY, -1),
@@ -806,18 +833,24 @@
private final SparseArray<Integer> mProcStatesCache = new SparseArray<>();
public boolean isUidForeground(int uid) {
- return mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
- <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ synchronized (this) {
+ return mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
+ <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ }
}
@Override
public void onUidGone(int uid, boolean disabled) {
- mProcStatesCache.delete(uid);
+ synchronized (this) {
+ mProcStatesCache.delete(uid);
+ }
}
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
- mProcStatesCache.put(uid, procState);
+ synchronized (this) {
+ mProcStatesCache.put(uid, procState);
+ }
}
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 45bd152..ace7777 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -97,7 +97,8 @@
new VibrationAttributes.Builder().build();
private static final int ATTRIBUTES_ALL_BYPASS_FLAGS =
VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
- | VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
+ | VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF
+ | VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE;
/** Fixed large duration used to note repeating vibrations to {@link IBatteryStats}. */
private static final long BATTERY_STATS_REPEATING_VIBRATION_DURATION = 5_000;
@@ -771,8 +772,11 @@
private Vibration.EndInfo startVibrationLocked(HalVibration vib) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
try {
- // Scale effect before dispatching it to the input devices or the vibration thread.
- vib.scaleEffects(mVibrationScaler::scale);
+ if (!vib.callerInfo.attrs.isFlagSet(
+ VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)) {
+ // Scale effect before dispatching it to the input devices or the vibration thread.
+ vib.scaleEffects(mVibrationScaler::scale);
+ }
boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable(
vib.callerInfo, vib.getEffectToPlay());
if (inputDevicesAvailable) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 4200fbf..c7a3c43 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -51,6 +51,7 @@
import android.app.IWallpaperManager;
import android.app.IWallpaperManagerCallback;
import android.app.PendingIntent;
+import android.app.UidObserver;
import android.app.UserSwitchObserver;
import android.app.WallpaperColors;
import android.app.WallpaperInfo;
@@ -103,6 +104,7 @@
import android.system.ErrnoException;
import android.system.Os;
import android.util.EventLog;
+import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -120,6 +122,7 @@
import com.android.server.SystemService;
import com.android.server.pm.UserManagerInternal;
import com.android.server.utils.TimingsTraceAndSlog;
+import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import org.xmlpull.v1.XmlPullParserException;
@@ -1645,6 +1648,40 @@
mWallpaperDisplayHelper = new WallpaperDisplayHelper(dm, mWindowManagerInternal);
mWallpaperCropper = new WallpaperCropper(mWallpaperDisplayHelper);
mActivityManager = mContext.getSystemService(ActivityManager.class);
+
+ if (mContext.getResources().getBoolean(
+ R.bool.config_pauseWallpaperRenderWhenStateChangeEnabled)) {
+ // Pause wallpaper rendering engine as soon as a performance impacted app is launched.
+ final String[] pauseRenderList = mContext.getResources().getStringArray(
+ R.array.pause_wallpaper_render_when_state_change);
+ final IntArray pauseRenderUids = new IntArray();
+ for (String pauseRenderApp : pauseRenderList) {
+ try {
+ int uid = mContext.getPackageManager().getApplicationInfo(
+ pauseRenderApp, 0).uid;
+ pauseRenderUids.add(uid);
+ } catch (Exception e) {
+ Slog.e(TAG, e.toString());
+ }
+ }
+ if (pauseRenderUids.size() > 0) {
+ try {
+ ActivityManager.getService().registerUidObserverForUids(new UidObserver() {
+ @Override
+ public void onUidStateChanged(int uid, int procState, long procStateSeq,
+ int capability) {
+ pauseOrResumeRenderingImmediately(
+ procState == ActivityManager.PROCESS_STATE_TOP);
+ }
+ }, ActivityManager.UID_OBSERVER_PROCSTATE,
+ ActivityManager.PROCESS_STATE_TOP, "android",
+ pauseRenderUids.toArray());
+ } catch (RemoteException e) {
+ Slog.e(TAG, e.toString());
+ }
+ }
+ }
+
mMonitor = new MyPackageMonitor();
mColorsChangedListeners = new SparseArray<>();
mWallpaperDataParser = new WallpaperDataParser(mContext, mWallpaperDisplayHelper,
@@ -2625,6 +2662,35 @@
}
}
+ private void pauseOrResumeRenderingImmediately(boolean pause) {
+ synchronized (mLock) {
+ final WallpaperData[] wallpapers = mIsLockscreenLiveWallpaperEnabled
+ ? getActiveWallpapers() : new WallpaperData[] {
+ mWallpaperMap.get(mCurrentUserId) };
+ for (WallpaperData data : wallpapers) {
+ if (data.connection == null || data.connection.mInfo == null) {
+ continue;
+ }
+ if (pause || LocalServices.getService(ActivityTaskManagerInternal.class)
+ .isUidForeground(data.connection.mInfo.getServiceInfo()
+ .applicationInfo.uid)) {
+ if (data.connection.containsDisplay(
+ mWindowManagerInternal.getTopFocusedDisplayId())) {
+ data.connection.forEachDisplayConnector(displayConnector -> {
+ if (displayConnector.mEngine != null) {
+ try {
+ displayConnector.mEngine.setVisibility(!pause);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to set visibility", e);
+ }
+ }
+ });
+ }
+ }
+ }
+ }
+ }
+
/**
* Propagate a wake event to the wallpaper engine(s).
*/
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index a01113b..9fa5ed2 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4030,6 +4030,9 @@
if (mAppStopped) {
abortAndClearOptionsAnimation();
}
+ if (mDisplayContent != null) {
+ mDisplayContent.mUnknownAppVisibilityController.appRemovedOrHidden(this);
+ }
}
boolean isFinishing() {
@@ -5388,12 +5391,6 @@
mLastDeferHidingClient = deferHidingClient;
if (!visible) {
- // If this activity is about to finish/stopped and now becomes invisible, remove it
- // from the unknownApp list in case the activity does not want to draw anything, which
- // keep the user waiting for the next transition to start.
- if (finishing || isState(STOPPED)) {
- displayContent.mUnknownAppVisibilityController.appRemovedOrHidden(this);
- }
// Because starting window was transferred, this activity may be a trampoline which has
// been occluded by next activity. If it has added windows, set client visibility
// immediately to avoid the client getting RELAYOUT_RES_FIRST_TIME from relayout and
@@ -5837,6 +5834,9 @@
break;
case STOPPED:
mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_STOPPED);
+ if (mDisplayContent != null) {
+ mDisplayContent.mUnknownAppVisibilityController.appRemovedOrHidden(this);
+ }
break;
case DESTROYED:
if (app != null && (mVisible || mVisibleRequested)) {
@@ -6517,7 +6517,6 @@
}
// Reset the last saved PiP snap fraction on app stop.
mDisplayContent.mPinnedTaskController.onActivityHidden(mActivityComponent);
- mDisplayContent.mUnknownAppVisibilityController.appRemovedOrHidden(this);
if (isClientVisible()) {
// Though this is usually unlikely to happen, still make sure the client is invisible.
setClientVisible(false);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index de335d3..c021785 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3002,7 +3002,7 @@
|| mLastResumedActivity == null) {
return;
}
- var userInfo = mUserManager.getUserInfo(mLastResumedActivity.mUserId);
+ var userInfo = getUserManager().getUserInfo(mLastResumedActivity.mUserId);
if (userInfo == null || !userInfo.isManagedProfile()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index 4444709..c79a8b6 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -378,8 +378,23 @@
if (!wc.isSyncFinished(this)) {
allFinished = false;
Slog.i(TAG, "Unfinished container: " + wc);
+ wc.forAllActivities(a -> {
+ if (a.isVisibleRequested()) {
+ if (a.isRelaunching()) {
+ Slog.i(TAG, " " + a + " is relaunching");
+ }
+ a.forAllWindows(w -> {
+ Slog.i(TAG, " " + w + " " + w.mWinAnimator.drawStateToString());
+ }, true /* traverseTopToBottom */);
+ } else if (a.mDisplayContent != null && !a.mDisplayContent
+ .mUnknownAppVisibilityController.allResolved()) {
+ Slog.i(TAG, " UnknownAppVisibility: " + a.mDisplayContent
+ .mUnknownAppVisibilityController.getDebugMessage());
+ }
+ });
}
}
+
for (int i = mDependencies.size() - 1; i >= 0; --i) {
allFinished = false;
Slog.i(TAG, "Unfinished dependency: " + mDependencies.get(i).mSyncId);
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 64a230e..823fbc9 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -28,6 +28,9 @@
* black layers of varying opacity at various Z-levels which create the effect of a Dim.
*/
public abstract class Dimmer {
+
+ static final boolean DIMMER_REFACTOR = Flags.dimmerRefactor();
+
/**
* The {@link WindowContainer} that our Dims are bounded to. We may be dimming on behalf of the
* host, some controller of it, or one of the hosts children.
@@ -40,7 +43,7 @@
// Constructs the correct type of dimmer
static Dimmer create(WindowContainer host) {
- return Flags.dimmerRefactor() ? new SmoothDimmer(host) : new LegacyDimmer(host);
+ return DIMMER_REFACTOR ? new SmoothDimmer(host) : new LegacyDimmer(host);
}
@NonNull
@@ -48,32 +51,34 @@
return mHost;
}
- protected abstract void dim(
- WindowContainer container, int relativeLayer, float alpha, int blurRadius);
+ /**
+ * Position the dim relatively to the dimming container.
+ * Normally called together with #setAppearance, it can be called alone to keep the dim parented
+ * to a visible container until the next dimming container is ready.
+ * If multiple containers call this method, only the changes relative to the topmost will be
+ * applied.
+ *
+ * For each call to {@link WindowContainer#prepareSurfaces()} the DimState will be reset, and
+ * the child of the host should call adjustRelativeLayer and {@link Dimmer#adjustAppearance} to
+ * continue dimming. Indeed, this method won't be able to keep dimming or get a new DimState
+ * without also adjusting the appearance.
+ * @param container The container which to dim above. Should be a child of the host.
+ * @param relativeLayer The position of the dim wrt the container
+ */
+ protected abstract void adjustRelativeLayer(WindowContainer container, int relativeLayer);
/**
- * Place a dim above the given container, which should be a child of the host container.
- * for each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset
- * and the child should call dimAbove again to request the Dim to continue.
- *
- * @param container The container which to dim above. Should be a child of our host.
- * @param alpha The alpha at which to Dim.
+ * Set the aspect of the dim layer, and request to keep dimming.
+ * For each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset, and the
+ * child should call setAppearance again to request the Dim to continue.
+ * If multiple containers call this method, only the changes relative to the topmost will be
+ * applied.
+ * @param container Container requesting the dim
+ * @param alpha Dim amount
+ * @param blurRadius Blur amount
*/
- void dimAbove(@NonNull WindowContainer container, float alpha) {
- dim(container, 1, alpha, 0);
- }
-
- /**
- * Like {@link #dimAbove} but places the dim below the given container.
- *
- * @param container The container which to dim below. Should be a child of our host.
- * @param alpha The alpha at which to Dim.
- * @param blurRadius The amount of blur added to the Dim.
- */
-
- void dimBelow(@NonNull WindowContainer container, float alpha, int blurRadius) {
- dim(container, -1, alpha, blurRadius);
- }
+ protected abstract void adjustAppearance(
+ WindowContainer container, float alpha, int blurRadius);
/**
* Mark all dims as pending completion on the next call to {@link #updateDims}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 02f5c21..cd114fc 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -68,6 +68,7 @@
private final Rect mTmpRect = new Rect();
private final InsetsStateController mStateController;
private final InsetsSourceControl mFakeControl;
+ private final Consumer<Transaction> mSetLeashPositionConsumer;
private @Nullable InsetsSourceControl mControl;
private @Nullable InsetsControlTarget mControlTarget;
private @Nullable InsetsControlTarget mPendingControlTarget;
@@ -85,16 +86,7 @@
private boolean mInsetsHintStale = true;
private @Flags int mFlagsFromFrameProvider;
private @Flags int mFlagsFromServer;
-
- private final Consumer<Transaction> mSetLeashPositionConsumer = t -> {
- if (mControl != null) {
- final SurfaceControl leash = mControl.getLeash();
- if (leash != null) {
- final Point position = mControl.getSurfacePosition();
- t.setPosition(leash, position.x, position.y);
- }
- }
- };
+ private boolean mHasPendingPosition;
/** The visibility override from the current controlling window. */
private boolean mClientVisible;
@@ -129,6 +121,21 @@
source.getId(), source.getType(), null /* leash */, false /* initialVisible */,
new Point(), Insets.NONE);
mControllable = (InsetsPolicy.CONTROLLABLE_TYPES & source.getType()) != 0;
+ mSetLeashPositionConsumer = t -> {
+ if (mControl != null) {
+ final SurfaceControl leash = mControl.getLeash();
+ if (leash != null) {
+ final Point position = mControl.getSurfacePosition();
+ t.setPosition(leash, position.x, position.y);
+ }
+ }
+ if (mHasPendingPosition) {
+ mHasPendingPosition = false;
+ if (mPendingControlTarget != mControlTarget) {
+ mStateController.notifyControlTargetChanged(mPendingControlTarget, this);
+ }
+ }
+ };
}
InsetsSource getSource() {
@@ -185,9 +192,8 @@
mWindowContainer.getInsetsSourceProviders().put(mSource.getId(), this);
if (mControllable) {
mWindowContainer.setControllableInsetProvider(this);
- if (mPendingControlTarget != null) {
+ if (mPendingControlTarget != mControlTarget) {
updateControlForTarget(mPendingControlTarget, true /* force */);
- mPendingControlTarget = null;
}
}
}
@@ -344,6 +350,7 @@
changed = true;
if (windowState != null && windowState.getWindowFrames().didFrameSizeChange()
&& windowState.mWinAnimator.getShown() && mWindowContainer.okToDisplay()) {
+ mHasPendingPosition = true;
windowState.applyWithNextDraw(mSetLeashPositionConsumer);
} else {
Transaction t = mWindowContainer.getSyncTransaction();
@@ -465,18 +472,23 @@
// to control the window for now.
return;
}
+ mPendingControlTarget = target;
if (mWindowContainer != null && mWindowContainer.getSurfaceControl() == null) {
// if window doesn't have a surface, set it null and return.
setWindowContainer(null, null, null);
}
if (mWindowContainer == null) {
- mPendingControlTarget = target;
return;
}
if (target == mControlTarget && !force) {
return;
}
+ if (mHasPendingPosition) {
+ // Don't create a new leash while having a pending position. Otherwise, the position
+ // will be changed earlier than expected, which can cause flicker.
+ return;
+ }
if (target == null) {
// Cancelling the animation will invoke onAnimationCancelled, resetting all the fields.
mWindowContainer.cancelAnimation();
@@ -618,6 +630,7 @@
}
pw.print(prefix);
pw.print("mIsLeashReadyForDispatching="); pw.print(mIsLeashReadyForDispatching);
+ pw.print("mHasPendingPosition="); pw.print(mHasPendingPosition);
pw.println();
if (mWindowContainer != null) {
pw.print(prefix + "mWindowContainer=");
@@ -631,7 +644,7 @@
pw.print(prefix + "mControlTarget=");
pw.println(mControlTarget);
}
- if (mPendingControlTarget != null) {
+ if (mPendingControlTarget != mControlTarget) {
pw.print(prefix + "mPendingControlTarget=");
pw.println(mPendingControlTarget);
}
@@ -652,7 +665,8 @@
if (mControlTarget != null && mControlTarget.getWindow() != null) {
mControlTarget.getWindow().dumpDebug(proto, CONTROL_TARGET, logLevel);
}
- if (mPendingControlTarget != null && mPendingControlTarget.getWindow() != null) {
+ if (mPendingControlTarget != null && mPendingControlTarget != mControlTarget
+ && mPendingControlTarget.getWindow() != null) {
mPendingControlTarget.getWindow().dumpDebug(proto, PENDING_CONTROL_TARGET, logLevel);
}
if (mFakeControlTarget != null && mFakeControlTarget.getWindow() != null) {
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 081ebe0..c4d0129 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -278,6 +278,12 @@
notifyPendingInsetsControlChanged();
}
+ void notifyControlTargetChanged(@Nullable InsetsControlTarget target,
+ InsetsSourceProvider provider) {
+ onControlTargetChanged(provider, target, false /* fake */);
+ notifyPendingInsetsControlChanged();
+ }
+
void notifyControlRevoked(@NonNull InsetsControlTarget previousControlTarget,
InsetsSourceProvider provider) {
removeFromControlMaps(previousControlTarget, provider, false /* fake */);
diff --git a/services/core/java/com/android/server/wm/LegacyDimmer.java b/services/core/java/com/android/server/wm/LegacyDimmer.java
index ccf956e..3265e60 100644
--- a/services/core/java/com/android/server/wm/LegacyDimmer.java
+++ b/services/core/java/com/android/server/wm/LegacyDimmer.java
@@ -134,8 +134,9 @@
boolean mAnimateExit = true;
/**
- * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for
- * details on Dim lifecycle.
+ * Used for Dims not associated with a WindowContainer.
+ * See {@link Dimmer#adjustRelativeLayer(WindowContainer, int)} for details on Dim
+ * lifecycle.
*/
boolean mDontReset;
SurfaceAnimator mSurfaceAnimator;
@@ -218,9 +219,8 @@
}
@Override
- protected void dim(WindowContainer container, int relativeLayer, float alpha, int blurRadius) {
+ protected void adjustAppearance(WindowContainer container, float alpha, int blurRadius) {
final DimState d = obtainDimState(container);
-
if (d == null) {
return;
}
@@ -229,14 +229,21 @@
// in the correct Z from lowest Z to highest. This ensures that the dim layer is always
// relative to the highest Z layer with a dim.
SurfaceControl.Transaction t = mHost.getPendingTransaction();
- t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
t.setAlpha(d.mDimLayer, alpha);
t.setBackgroundBlurRadius(d.mDimLayer, blurRadius);
-
d.mDimming = true;
}
@Override
+ protected void adjustRelativeLayer(WindowContainer container, int relativeLayer) {
+ final DimState d = mDimState;
+ if (d != null) {
+ SurfaceControl.Transaction t = mHost.getPendingTransaction();
+ t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
+ }
+ }
+
+ @Override
boolean updateDims(SurfaceControl.Transaction t) {
if (mDimState == null) {
return false;
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index f6c3640..f8c39d0 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -16,6 +16,7 @@
mariiasand@google.com
rgl@google.com
yunfanc@google.com
+wilsonshih@google.com
per-file BackgroundActivityStartController.java = set noparent
per-file BackgroundActivityStartController.java = brufino@google.com, topjohnwu@google.com, achim@google.com, ogunwale@google.com, louischang@google.com, lus@google.com
diff --git a/services/core/java/com/android/server/wm/SmoothDimmer.java b/services/core/java/com/android/server/wm/SmoothDimmer.java
index 6ddbd2c..2549bbf 100644
--- a/services/core/java/com/android/server/wm/SmoothDimmer.java
+++ b/services/core/java/com/android/server/wm/SmoothDimmer.java
@@ -63,8 +63,9 @@
boolean mAnimateExit = true;
/**
- * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for
- * details on Dim lifecycle.
+ * Used for Dims not associated with a WindowContainer.
+ * See {@link Dimmer#adjustRelativeLayer(WindowContainer, int)} for details on Dim
+ * lifecycle.
*/
boolean mDontReset;
@@ -105,22 +106,34 @@
}
void setExitParameters(WindowContainer container) {
- setRequestedParameters(container, -1, 0, 0);
+ setRequestedRelativeParent(container, -1 /* relativeLayer */);
+ setRequestedAppearance(0f /* alpha */, 0 /* blur */);
}
+
// Sets a requested change without applying it immediately
- void setRequestedParameters(WindowContainer container, int relativeLayer, float alpha,
- int blurRadius) {
- mRequestedProperties.mDimmingContainer = container;
+ void setRequestedRelativeParent(WindowContainer relativeParent, int relativeLayer) {
+ mRequestedProperties.mDimmingContainer = relativeParent;
mRequestedProperties.mRelativeLayer = relativeLayer;
+ }
+
+ // Sets a requested change without applying it immediately
+ void setRequestedAppearance(float alpha, int blurRadius) {
mRequestedProperties.mAlpha = alpha;
mRequestedProperties.mBlurRadius = blurRadius;
}
/**
* Commit the last changes we received. Called after
- * {@link Change#setRequestedParameters(WindowContainer, int, float, int)}
+ * {@link Change#setExitParameters(WindowContainer)},
+ * {@link Change#setRequestedRelativeParent(WindowContainer, int)}, or
+ * {@link Change#setRequestedAppearance(float, int)}
*/
void applyChanges(SurfaceControl.Transaction t) {
+ if (mRequestedProperties.mDimmingContainer == null) {
+ Log.e(TAG, this + " does not have a dimming container. Have you forgotten to "
+ + "call adjustRelativeLayer?");
+ return;
+ }
if (mRequestedProperties.mDimmingContainer.mSurfaceControl == null) {
Log.w(TAG, "container " + mRequestedProperties.mDimmingContainer
+ "does not have a surface");
@@ -276,14 +289,19 @@
}
@Override
- protected void dim(WindowContainer container, int relativeLayer, float alpha, int blurRadius) {
+ protected void adjustAppearance(WindowContainer container, float alpha, int blurRadius) {
final DimState d = obtainDimState(container);
-
- mDimState.mRequestedProperties.mDimmingContainer = container;
- mDimState.setRequestedParameters(container, relativeLayer, alpha, blurRadius);
+ mDimState.setRequestedAppearance(alpha, blurRadius);
d.mDimming = true;
}
+ @Override
+ protected void adjustRelativeLayer(WindowContainer container, int relativeLayer) {
+ if (mDimState != null) {
+ mDimState.setRequestedRelativeParent(container, relativeLayer);
+ }
+ }
+
boolean updateDims(SurfaceControl.Transaction t) {
if (mDimState == null) {
return false;
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index de7871e..8ac21e4 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -757,7 +757,7 @@
final TransitionRequestInfo request = new TransitionRequestInfo(transition.mType,
startTaskInfo, pipTaskInfo, remoteTransition, displayChange,
- transition.getFlags());
+ transition.getFlags(), transition.getSyncId());
transition.mLogger.mRequestTimeNs = SystemClock.elapsedRealtimeNanos();
transition.mLogger.mRequest = request;
@@ -1592,8 +1592,8 @@
TransitionInfo mInfo;
private String buildOnSendLog() {
- StringBuilder sb = new StringBuilder("Sent Transition #").append(mSyncId)
- .append(" createdAt=").append(TimeUtils.logTimeOfDay(mCreateWallTimeMs));
+ StringBuilder sb = new StringBuilder("Sent Transition (#").append(mSyncId)
+ .append(") createdAt=").append(TimeUtils.logTimeOfDay(mCreateWallTimeMs));
if (mRequest != null) {
sb.append(" via request=").append(mRequest);
}
@@ -1617,7 +1617,8 @@
void logOnSend() {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "%s", buildOnSendLog());
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, " startWCT=%s", mStartWCT);
- ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, " info=%s", mInfo);
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, " info=%s",
+ mInfo.toString(" " /* prefix */));
}
private static String toMsString(long nanos) {
@@ -1625,8 +1626,8 @@
}
private String buildOnFinishLog() {
- StringBuilder sb = new StringBuilder("Finish Transition #").append(mSyncId)
- .append(": created at ").append(TimeUtils.logTimeOfDay(mCreateWallTimeMs));
+ StringBuilder sb = new StringBuilder("Finish Transition (#").append(mSyncId)
+ .append("): created at ").append(TimeUtils.logTimeOfDay(mCreateWallTimeMs));
sb.append(" collect-started=").append(toMsString(mCollectTimeNs - mCreateTimeNs));
if (mRequestTimeNs != 0) {
sb.append(" request-sent=").append(toMsString(mRequestTimeNs - mCreateTimeNs));
diff --git a/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java b/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java
index 41c1e79..c071396 100644
--- a/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java
+++ b/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java
@@ -70,6 +70,9 @@
}
boolean isVisibilityUnknown(ActivityRecord r) {
+ if (mUnknownApps.isEmpty()) {
+ return false;
+ }
return mUnknownApps.containsKey(r);
}
@@ -90,6 +93,9 @@
}
void appRemovedOrHidden(@NonNull ActivityRecord activity) {
+ if (mUnknownApps.isEmpty()) {
+ return;
+ }
if (DEBUG_UNKNOWN_APP_VISIBILITY) {
Slog.d(TAG, "App removed or hidden activity=" + activity);
}
@@ -117,8 +123,11 @@
* Notifies that {@param activity} has finished resuming.
*/
void notifyAppResumedFinished(@NonNull ActivityRecord activity) {
- if (mUnknownApps.containsKey(activity)
- && mUnknownApps.get(activity) == UNKNOWN_STATE_WAITING_RESUME) {
+ if (mUnknownApps.isEmpty()) {
+ return;
+ }
+ final Integer state = mUnknownApps.get(activity);
+ if (state != null && state == UNKNOWN_STATE_WAITING_RESUME) {
if (DEBUG_UNKNOWN_APP_VISIBILITY) {
Slog.d(TAG, "App resume finished activity=" + activity);
}
@@ -130,13 +139,16 @@
* Notifies that {@param activity} has relaid out.
*/
void notifyRelayouted(@NonNull ActivityRecord activity) {
- if (!mUnknownApps.containsKey(activity)) {
+ if (mUnknownApps.isEmpty()) {
+ return;
+ }
+ final Integer state = mUnknownApps.get(activity);
+ if (state == null) {
return;
}
if (DEBUG_UNKNOWN_APP_VISIBILITY) {
Slog.d(TAG, "App relayouted appWindow=" + activity);
}
- int state = mUnknownApps.get(activity);
if (state == UNKNOWN_STATE_WAITING_RELAYOUT || activity.mStartingWindow != null) {
mUnknownApps.put(activity, UNKNOWN_STATE_WAITING_VISIBILITY_UPDATE);
mDisplayContent.notifyKeyguardFlagsChanged();
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 674ff48..94e66ff 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -536,7 +536,7 @@
window.mWallpaperY = y;
window.mWallpaperXStep = xStep;
window.mWallpaperYStep = yStep;
- updateWallpaperOffsetLocked(window, true);
+ updateWallpaperOffsetLocked(window, !mService.mFlags.mWallpaperOffsetAsync);
}
}
@@ -561,7 +561,7 @@
if (window.mWallpaperDisplayOffsetX != x || window.mWallpaperDisplayOffsetY != y) {
window.mWallpaperDisplayOffsetX = x;
window.mWallpaperDisplayOffsetY = y;
- updateWallpaperOffsetLocked(window, true);
+ updateWallpaperOffsetLocked(window, !mService.mFlags.mWallpaperOffsetAsync);
}
}
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 50ef52a..1ed1431 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -117,7 +117,8 @@
final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
final WindowState wallpaper = mChildren.get(wallpaperNdx);
- if (wallpaperController.updateWallpaperOffset(wallpaper, sync)) {
+ if (wallpaperController.updateWallpaperOffset(wallpaper,
+ sync && !mWmService.mFlags.mWallpaperOffsetAsync)) {
// We only want to be synchronous with one wallpaper.
sync = false;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerFlags.java b/services/core/java/com/android/server/wm/WindowManagerFlags.java
index 5b9acb2..4667710 100644
--- a/services/core/java/com/android/server/wm/WindowManagerFlags.java
+++ b/services/core/java/com/android/server/wm/WindowManagerFlags.java
@@ -47,5 +47,7 @@
final boolean mWindowStateResizeItemFlag = Flags.windowStateResizeItemFlag();
+ final boolean mWallpaperOffsetAsync = Flags.wallpaperOffsetAsync();
+
/* End Available Flags */
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 7f36aec..3a793e9 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5136,8 +5136,8 @@
private void applyDims() {
if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || shouldDrawBlurBehind())
- && mToken.isVisibleRequested() && isVisibleNow() && !mHidden
- && mTransitionController.canApplyDim(getTask())) {
+ && (Dimmer.DIMMER_REFACTOR ? mWinAnimator.getShown() : isVisibleNow())
+ && !mHidden && mTransitionController.canApplyDim(getTask())) {
// Only show the Dimmer when the following is satisfied:
// 1. The window has the flag FLAG_DIM_BEHIND or blur behind is requested
// 2. The WindowToken is not hidden so dims aren't shown when the window is exiting.
@@ -5147,7 +5147,13 @@
mIsDimming = true;
final float dimAmount = (mAttrs.flags & FLAG_DIM_BEHIND) != 0 ? mAttrs.dimAmount : 0;
final int blurRadius = shouldDrawBlurBehind() ? mAttrs.getBlurBehindRadius() : 0;
- getDimmer().dimBelow(this, dimAmount, blurRadius);
+ // If the window is visible from surface flinger perspective (mWinAnimator.getShown())
+ // but not window manager visible (!isVisibleNow()), it can still be the parent of the
+ // dim, but can not create a new surface or continue a dim alone.
+ if (isVisibleNow()) {
+ getDimmer().adjustAppearance(this, dimAmount, blurRadius);
+ }
+ getDimmer().adjustRelativeLayer(this, -1 /* relativeLayer */);
}
}
@@ -5207,12 +5213,17 @@
void prepareSurfaces() {
mIsDimming = false;
if (mHasSurface) {
- applyDims();
+ if (!Dimmer.DIMMER_REFACTOR) {
+ applyDims();
+ }
updateSurfacePositionNonOrganized();
// Send information to SurfaceFlinger about the priority of the current window.
updateFrameRateSelectionPriorityIfNeeded();
updateScaleIfNeeded();
mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
+ if (Dimmer.DIMMER_REFACTOR) {
+ applyDims();
+ }
}
super.prepareSurfaces();
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 709d5e3..24ee163 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -189,7 +189,7 @@
"android.hardware.thermal@1.0",
"android.hardware.thermal-V1-ndk",
"android.hardware.tv.input@1.0",
- "android.hardware.tv.input-V1-ndk",
+ "android.hardware.tv.input-V2-ndk",
"android.hardware.vibrator-V2-cpp",
"android.hardware.vibrator@1.0",
"android.hardware.vibrator@1.1",
@@ -244,5 +244,5 @@
filegroup {
name: "lib_oomConnection_native",
- srcs: ["com_android_server_am_OomConnection.cpp",],
+ srcs: ["com_android_server_am_OomConnection.cpp"],
}
diff --git a/services/core/jni/tvinput/JTvInputHal.cpp b/services/core/jni/tvinput/JTvInputHal.cpp
index c736617..dc05462 100644
--- a/services/core/jni/tvinput/JTvInputHal.cpp
+++ b/services/core/jni/tvinput/JTvInputHal.cpp
@@ -368,12 +368,20 @@
}
JTvInputHal::TvMessageEventWrapper JTvInputHal::TvMessageEventWrapper::createEventWrapper(
- const AidlTvMessageEvent& aidlTvMessageEvent) {
+ const AidlTvMessageEvent& aidlTvMessageEvent, bool isLegacyMessage) {
+ auto messageList = aidlTvMessageEvent.messages;
TvMessageEventWrapper event;
- event.messages.insert(event.messages.begin(), std::begin(aidlTvMessageEvent.messages) + 1,
- std::end(aidlTvMessageEvent.messages));
+ // Handle backwards compatibility for V1
+ if (isLegacyMessage) {
+ event.deviceId = messageList[0].groupId;
+ event.messages.insert(event.messages.begin(), std::begin(messageList) + 1,
+ std::end(messageList));
+ } else {
+ event.deviceId = aidlTvMessageEvent.deviceId;
+ event.messages.insert(event.messages.begin(), std::begin(messageList),
+ std::end(messageList));
+ }
event.streamId = aidlTvMessageEvent.streamId;
- event.deviceId = aidlTvMessageEvent.messages[0].groupId;
event.type = aidlTvMessageEvent.type;
return event;
}
@@ -449,15 +457,30 @@
::ndk::ScopedAStatus JTvInputHal::TvInputCallback::notifyTvMessageEvent(
const AidlTvMessageEvent& event) {
const std::string DEVICE_ID_SUBTYPE = "device_id";
- if (event.messages.size() > 1 && event.messages[0].subType == DEVICE_ID_SUBTYPE) {
- mHal->mLooper
- ->sendMessage(new NotifyTvMessageHandler(mHal,
- TvMessageEventWrapper::createEventWrapper(
- event)),
- static_cast<int>(event.type));
+ ::ndk::ScopedAStatus status = ::ndk::ScopedAStatus::ok();
+ int32_t aidlVersion = 0;
+ if (mHal->mTvInput->getAidlInterfaceVersion(&aidlVersion).isOk() && event.messages.size() > 0) {
+ bool validLegacyMessage = aidlVersion == 1 &&
+ event.messages[0].subType == DEVICE_ID_SUBTYPE && event.messages.size() > 1;
+ bool validTvMessage = aidlVersion > 1 && event.messages.size() > 0;
+ if (validLegacyMessage || validTvMessage) {
+ mHal->mLooper->sendMessage(
+ new NotifyTvMessageHandler(mHal,
+ TvMessageEventWrapper::
+ createEventWrapper(event,
+ validLegacyMessage)),
+ static_cast<int>(event.type));
+ } else {
+ status = ::ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ ALOGE("The TVMessage event was malformed for HAL version: %d", aidlVersion);
+ }
+ } else {
+ status = ::ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ ALOGE("The TVMessage event was empty or the HAL version (version: %d) could not "
+ "be inferred.",
+ aidlVersion);
}
-
- return ::ndk::ScopedAStatus::ok();
+ return status;
}
JTvInputHal::ITvInputWrapper::ITvInputWrapper(std::shared_ptr<AidlITvInput>& aidlTvInput)
@@ -521,4 +544,12 @@
}
}
+::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::getAidlInterfaceVersion(int32_t* _aidl_return) {
+ if (mIsHidl) {
+ return ::ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ } else {
+ return mAidlTvInput->getInterfaceVersion(_aidl_return);
+ }
+}
+
} // namespace android
diff --git a/services/core/jni/tvinput/JTvInputHal.h b/services/core/jni/tvinput/JTvInputHal.h
index 1d8d162..6026a10 100644
--- a/services/core/jni/tvinput/JTvInputHal.h
+++ b/services/core/jni/tvinput/JTvInputHal.h
@@ -138,7 +138,7 @@
TvMessageEventWrapper() {}
static TvMessageEventWrapper createEventWrapper(
- const AidlTvMessageEvent& aidlTvMessageEvent);
+ const AidlTvMessageEvent& aidlTvMessageEvent, bool isLegacyMessage);
int streamId;
int deviceId;
@@ -195,6 +195,7 @@
::ndk::ScopedAStatus getTvMessageQueueDesc(
MQDescriptor<int8_t, SynchronizedReadWrite>* out_queue, int32_t in_deviceId,
int32_t in_streamId);
+ ::ndk::ScopedAStatus getAidlInterfaceVersion(int32_t* _aidl_return);
private:
::ndk::ScopedAStatus hidlSetCallback(const std::shared_ptr<TvInputCallback>& in_callback);
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index debd891..215934f 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -272,12 +272,12 @@
<xs:element name="brightnessDecreaseDebounceMillis" type="xs:nonNegativeInteger">
<xs:annotation name="final"/>
</xs:element>
- <!-- Animation time for brightness increase in millis -->
- <xs:element name="brightnessIncreaseDurationMillis" type="xs:nonNegativeInteger">
+ <!-- Animation speed for brightness increase. In framework brightness units per second. -->
+ <xs:element name="screenBrightnessRampIncrease" type="nonNegativeDecimal">
<xs:annotation name="final"/>
</xs:element>
- <!-- Animation time for brightness decrease in millis -->
- <xs:element name="brightnessDecreaseDurationMillis" type="xs:nonNegativeInteger">
+ <!-- Animation speed for brightness decrease. In framework brightness units per second. -->
+ <xs:element name="screenBrightnessRampDecrease" type="nonNegativeDecimal">
<xs:annotation name="final"/>
</xs:element>
</xs:complexType>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 2d27f0c..f7e0043 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -179,15 +179,15 @@
public class HdrBrightnessConfig {
ctor public HdrBrightnessConfig();
method public final java.math.BigInteger getBrightnessDecreaseDebounceMillis();
- method public final java.math.BigInteger getBrightnessDecreaseDurationMillis();
method public final java.math.BigInteger getBrightnessIncreaseDebounceMillis();
- method public final java.math.BigInteger getBrightnessIncreaseDurationMillis();
method @NonNull public final com.android.server.display.config.NonNegativeFloatToFloatMap getBrightnessMap();
+ method public final java.math.BigDecimal getScreenBrightnessRampDecrease();
+ method public final java.math.BigDecimal getScreenBrightnessRampIncrease();
method public final void setBrightnessDecreaseDebounceMillis(java.math.BigInteger);
- method public final void setBrightnessDecreaseDurationMillis(java.math.BigInteger);
method public final void setBrightnessIncreaseDebounceMillis(java.math.BigInteger);
- method public final void setBrightnessIncreaseDurationMillis(java.math.BigInteger);
method public final void setBrightnessMap(@NonNull com.android.server.display.config.NonNegativeFloatToFloatMap);
+ method public final void setScreenBrightnessRampDecrease(java.math.BigDecimal);
+ method public final void setScreenBrightnessRampIncrease(java.math.BigDecimal);
}
public class HighBrightnessMode {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 25e8475..922f69c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -40,6 +40,7 @@
import android.app.admin.PolicyValue;
import android.app.admin.TargetUser;
import android.app.admin.UserRestrictionPolicyKey;
+import android.app.admin.flags.FlagUtils;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -65,7 +66,6 @@
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
-import com.android.server.devicepolicy.flags.FlagUtils;
import com.android.server.utils.Slogf;
import libcore.io.IoUtils;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 5a620a3..8509155 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -330,6 +330,7 @@
import android.app.admin.UnsafeStateException;
import android.app.admin.UserRestrictionPolicyKey;
import android.app.admin.WifiSsidPolicy;
+import android.app.admin.flags.FlagUtils;
import android.app.backup.IBackupManager;
import android.app.compat.CompatChanges;
import android.app.role.OnRoleHoldersChangedListener;
@@ -490,7 +491,6 @@
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
import com.android.server.devicepolicy.ActiveAdmin.TrustAgentInfo;
-import com.android.server.devicepolicy.flags.FlagUtils;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pdb.PersistentDataBlockManagerInternal;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/flags/Android.bp b/services/devicepolicy/java/com/android/server/devicepolicy/flags/Android.bp
deleted file mode 100644
index 1a45782..0000000
--- a/services/devicepolicy/java/com/android/server/devicepolicy/flags/Android.bp
+++ /dev/null
@@ -1,16 +0,0 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-aconfig_declarations {
- name: "device_policy_aconfig_flags",
- package: "com.android.server.devicepolicy.flags",
- srcs: [
- "flags.aconfig",
- ],
-}
-
-java_aconfig_library {
- name: "device_policy_aconfig_flags_lib",
- aconfig_declarations: "device_policy_aconfig_flags",
-}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/flags/flags.aconfig b/services/devicepolicy/java/com/android/server/devicepolicy/flags/flags.aconfig
deleted file mode 100644
index 0dde496..0000000
--- a/services/devicepolicy/java/com/android/server/devicepolicy/flags/flags.aconfig
+++ /dev/null
@@ -1,14 +0,0 @@
-package: "com.android.server.devicepolicy.flags"
-
-flag {
- name: "policy_engine_migration_v2_enabled"
- namespace: "enterprise"
- description: "V2 of the policy engine migrations for Android V"
- bug: "289520697"
-}
-flag {
- name: "device_policy_size_tracking_enabled"
- namespace: "enterprise"
- description: "Add feature to track the total policy size and have a max threshold."
- bug: "281543351"
-}
\ No newline at end of file
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index c26aee8..924e2f8 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -338,6 +338,8 @@
"com.android.clockwork.modes.ModeManagerService";
private static final String WEAR_DISPLAY_SERVICE_CLASS =
"com.android.clockwork.display.WearDisplayService";
+ private static final String WEAR_DEBUG_SERVICE_CLASS =
+ "com.android.clockwork.debug.WearDebugService";
private static final String WEAR_TIME_SERVICE_CLASS =
"com.android.clockwork.time.WearTimeService";
private static final String WEAR_SETTINGS_SERVICE_CLASS =
@@ -2636,6 +2638,12 @@
mSystemServiceManager.startService(WEAR_DISPLAY_SERVICE_CLASS);
t.traceEnd();
+ if (Build.IS_DEBUGGABLE) {
+ t.traceBegin("StartWearDebugService");
+ mSystemServiceManager.startService(WEAR_DEBUG_SERVICE_CLASS);
+ t.traceEnd();
+ }
+
t.traceBegin("StartWearTimeService");
mSystemServiceManager.startService(WEAR_TIME_SERVICE_CLASS);
t.traceEnd();
diff --git a/services/midi/Android.bp b/services/midi/Android.bp
index 4b5f8a7..a385fe3 100644
--- a/services/midi/Android.bp
+++ b/services/midi/Android.bp
@@ -18,8 +18,8 @@
name: "services.midi",
defaults: ["platform_service_defaults"],
srcs: [":services.midi-sources"],
- libs: ["services.core"],
- static_libs: [
+ libs: [
+ "services.core",
"aconfig_midi_flags_java_lib",
],
}
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index f3ac7d5..12cd0f6 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -143,8 +143,8 @@
val result: Result,
val componentName: ComponentName? = ComponentName(pkgName, COMPONENT_CLASS_NAME)
) {
- constructor(pkgName: String, appType: AppType, exception: Class<out Exception>)
- : this(pkgName, appType, Result.Exception(exception))
+ constructor(pkgName: String, appType: AppType, exception: Class<out Exception>) :
+ this(pkgName, appType, Result.Exception(exception))
val expectedLabel = when (result) {
Result.Changed, Result.ChangedWithoutNotify, Result.NotChanged -> TEST_LABEL
@@ -299,11 +299,9 @@
.hideAsFinal()
private fun makePkgSetting(pkgName: String, pkg: AndroidPackageInternal) =
- PackageSetting(
- pkgName, null, File("/test"),
- null, null, null, null, 0, 0, 0, 0, null, null, null, null, null,
- UUID.fromString("3f9d52b7-d7b4-406a-a1da-d9f19984c72c")
- ).apply {
+ PackageSetting(pkgName, null, File("/test"), 0, 0,
+ UUID.fromString("3f9d52b7-d7b4-406a-a1da-d9f19984c72c"))
+ .apply {
if (params.isSystem) {
this.flags = this.flags or ApplicationInfo.FLAG_SYSTEM
}
@@ -373,7 +371,7 @@
whenever(this.isCallerRecents(anyInt())) { false }
}
val mockAppsFilter: AppsFilterImpl = mockThrowOnUnmocked {
- whenever(this.shouldFilterApplication(any<PackageDataSnapshot>(), anyInt(),
+ whenever(this.shouldFilterApplication(any<PackageDataSnapshot>(), anyInt(),
any<PackageSetting>(), any<PackageSetting>(), anyInt())) { false }
whenever(this.snapshot()) { this@mockThrowOnUnmocked }
whenever(registerObserver(any())).thenCallRealMethod()
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
index cbedcaf..aaad669 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -967,20 +967,12 @@
PACKAGE_NAME,
REAL_PACKAGE_NAME,
INITIAL_CODE_PATH /*codePath*/,
- null /*legacyNativeLibraryPathString*/,
- "x86_64" /*primaryCpuAbiString*/,
- "x86" /*secondaryCpuAbiString*/,
- null /*cpuAbiOverrideString*/,
- INITIAL_VERSION_CODE,
ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_HAS_CODE,
ApplicationInfo.PRIVATE_FLAG_PRIVILEGED|ApplicationInfo.PRIVATE_FLAG_HIDDEN,
- 0,
- null /*usesSdkLibraries*/,
- null /*usesSdkLibrariesVersions*/,
- null /*usesStaticLibraries*/,
- null /*usesStaticLibrariesVersions*/,
- null /*mimeGroups*/,
- UUID.randomUUID());
+ UUID.randomUUID())
+ .setPrimaryCpuAbi("x86_64")
+ .setSecondaryCpuAbi("x86")
+ .setLongVersionCode(INITIAL_VERSION_CODE);
origPkgSetting01.setPkg(mockAndroidPackage(origPkgSetting01));
final PackageSetting testPkgSetting01 = new PackageSetting(origPkgSetting01);
verifySettingCopy(origPkgSetting01, testPkgSetting01);
@@ -989,23 +981,15 @@
@Test
public void testPackageStateCopy02() {
final PackageSetting origPkgSetting01 = new PackageSetting(
- PACKAGE_NAME /*pkgName*/,
- REAL_PACKAGE_NAME /*realPkgName*/,
+ PACKAGE_NAME,
+ REAL_PACKAGE_NAME,
INITIAL_CODE_PATH /*codePath*/,
- null /*legacyNativeLibraryPathString*/,
- "x86_64" /*primaryCpuAbiString*/,
- "x86" /*secondaryCpuAbiString*/,
- null /*cpuAbiOverrideString*/,
- INITIAL_VERSION_CODE,
ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_HAS_CODE,
ApplicationInfo.PRIVATE_FLAG_PRIVILEGED|ApplicationInfo.PRIVATE_FLAG_HIDDEN,
- 0,
- null /*usesSdkLibraries*/,
- null /*usesSdkLibrariesVersions*/,
- null /*usesStaticLibraries*/,
- null /*usesStaticLibrariesVersions*/,
- null /*mimeGroups*/,
- UUID.randomUUID());
+ UUID.randomUUID())
+ .setPrimaryCpuAbi("x86_64")
+ .setSecondaryCpuAbi("x86")
+ .setLongVersionCode(INITIAL_VERSION_CODE);
origPkgSetting01.setUserState(0, 100, 100, 1, true, false, false, false, 0, null, false,
false, "lastDisabledCaller", new ArraySet<>(new String[]{"enabledComponent1"}),
new ArraySet<>(new String[]{"disabledComponent1"}), 0, 0, "harmfulAppWarning",
@@ -1028,20 +1012,10 @@
PACKAGE_NAME /*pkgName*/,
REAL_PACKAGE_NAME /*realPkgName*/,
UPDATED_CODE_PATH /*codePath*/,
- null /*legacyNativeLibraryPathString*/,
- null /*primaryCpuAbiString*/,
- null /*secondaryCpuAbiString*/,
- null /*cpuAbiOverrideString*/,
- UPDATED_VERSION_CODE,
0 /*pkgFlags*/,
0 /*pkgPrivateFlags*/,
- 0,
- null /*usesSdkLibraries*/,
- null /*usesSdkLibrariesVersions*/,
- null /*usesStaticLibraries*/,
- null /*usesStaticLibrariesVersions*/,
- null /*mimeGroups*/,
- UUID.randomUUID());
+ UUID.randomUUID())
+ .setLongVersionCode(UPDATED_VERSION_CODE);
testPkgSetting01.copyPackageSetting(origPkgSetting01, true);
verifySettingCopy(origPkgSetting01, testPkgSetting01);
verifyUserStatesCopy(origPkgSetting01.readUserState(0),
@@ -1717,20 +1691,13 @@
PACKAGE_NAME,
REAL_PACKAGE_NAME,
INITIAL_CODE_PATH /*codePath*/,
- null /*legacyNativeLibraryPathString*/,
- "x86_64" /*primaryCpuAbiString*/,
- "x86" /*secondaryCpuAbiString*/,
- null /*cpuAbiOverrideString*/,
- INITIAL_VERSION_CODE,
pkgFlags,
0 /*privateFlags*/,
- sharedUserId,
- null /*usesSdkLibraries*/,
- null /*usesSdkLibrariesVersions*/,
- null /*usesStaticLibraries*/,
- null /*usesStaticLibrariesVersions*/,
- null /*mimeGroups*/,
- UUID.randomUUID());
+ UUID.randomUUID())
+ .setPrimaryCpuAbi("x86_64")
+ .setSecondaryCpuAbi("x86")
+ .setLongVersionCode(INITIAL_VERSION_CODE)
+ .setSharedUserAppId(sharedUserId);
}
private PackageSetting createPackageSetting(String packageName) {
@@ -1738,20 +1705,12 @@
packageName,
packageName,
INITIAL_CODE_PATH /*codePath*/,
- null /*legacyNativeLibraryPathString*/,
- "x86_64" /*primaryCpuAbiString*/,
- "x86" /*secondaryCpuAbiString*/,
- null /*cpuAbiOverrideString*/,
- INITIAL_VERSION_CODE,
0,
0 /*privateFlags*/,
- 0,
- null /*usesSdkLibraries*/,
- null /*usesSdkLibrariesVersions*/,
- null /*usesStaticLibraries*/,
- null /*usesStaticLibrariesVersions*/,
- null /*mimeGroups*/,
- UUID.randomUUID());
+ UUID.randomUUID())
+ .setPrimaryCpuAbi("x86_64")
+ .setSecondaryCpuAbi("x86")
+ .setLongVersionCode(INITIAL_VERSION_CODE);
}
static @NonNull List<UserInfo> createFakeUsers() {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
index c12aedb..a400f12 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
@@ -90,7 +90,9 @@
.append("\n isSlowChange:")
.append(displayBrightnessState.isSlowChange())
.append("\n maxBrightness:")
- .append(displayBrightnessState.getMaxBrightness());
+ .append(displayBrightnessState.getMaxBrightness())
+ .append("\n customAnimationRate:")
+ .append(displayBrightnessState.getCustomAnimationRate());
return sb.toString();
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index c37d21a..179a9d5 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -570,9 +570,9 @@
assertNotNull(data);
assertEquals(2, data.mMaxBrightnessLimits.size());
assertEquals(13000, data.mBrightnessDecreaseDebounceMillis);
- assertEquals(10000, data.mBrightnessDecreaseDurationMillis);
+ assertEquals(0.1f, data.mScreenBrightnessRampDecrease, SMALL_DELTA);
assertEquals(1000, data.mBrightnessIncreaseDebounceMillis);
- assertEquals(11000, data.mBrightnessIncreaseDurationMillis);
+ assertEquals(0.11f, data.mScreenBrightnessRampIncrease, SMALL_DELTA);
assertEquals(0.3f, data.mMaxBrightnessLimits.get(500f), SMALL_DELTA);
assertEquals(0.6f, data.mMaxBrightnessLimits.get(1200f), SMALL_DELTA);
@@ -841,9 +841,9 @@
+ " </point>\n"
+ " </brightnessMap>\n"
+ " <brightnessIncreaseDebounceMillis>1000</brightnessIncreaseDebounceMillis>\n"
- + " <brightnessIncreaseDurationMillis>11000</brightnessIncreaseDurationMillis>\n"
+ + " <screenBrightnessRampIncrease>0.11</screenBrightnessRampIncrease>\n"
+ " <brightnessDecreaseDebounceMillis>13000</brightnessDecreaseDebounceMillis>\n"
- + " <brightnessDecreaseDurationMillis>10000</brightnessDecreaseDurationMillis>\n"
+ + " <screenBrightnessRampDecrease>0.1</screenBrightnessRampDecrease>\n"
+ "</hdrBrightnessConfig>";
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
index 8b54d6d2..47521d1 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -74,6 +74,7 @@
import com.android.server.am.BatteryStatsService;
import com.android.server.display.RampAnimator.DualRampAnimator;
import com.android.server.display.brightness.BrightnessEvent;
+import com.android.server.display.brightness.clamper.BrightnessClamperController;
import com.android.server.display.brightness.clamper.HdrClamper;
import com.android.server.display.color.ColorDisplayService;
import com.android.server.display.feature.DisplayManagerFlags;
@@ -1314,6 +1315,54 @@
}
@Test
+ public void testRampRateForClampersControllerApplied() {
+ float transitionRate = 1.5f;
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f);
+ when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f);
+ when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer(
+ invocation -> DisplayBrightnessState.builder()
+ .setIsSlowChange(invocation.getArgument(2))
+ .setBrightness(invocation.getArgument(1))
+ .setMaxBrightness(PowerManager.BRIGHTNESS_MAX)
+ .setCustomAnimationRate(transitionRate).build());
+
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator, atLeastOnce()).animateTo(anyFloat(), anyFloat(),
+ eq(transitionRate), anyBoolean());
+ }
+
+ @Test
+ public void testRampRateForClampersControllerNotApplied_ifDoze() {
+ float transitionRate = 1.5f;
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+ dpr.dozeScreenState = Display.STATE_UNKNOWN;
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f);
+ when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f);
+ when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer(
+ invocation -> DisplayBrightnessState.builder()
+ .setIsSlowChange(invocation.getArgument(2))
+ .setBrightness(invocation.getArgument(1))
+ .setMaxBrightness(PowerManager.BRIGHTNESS_MAX)
+ .setCustomAnimationRate(transitionRate).build());
+
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.animator, atLeastOnce()).animateTo(anyFloat(), anyFloat(),
+ eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), anyBoolean());
+ verify(mHolder.animator, never()).animateTo(anyFloat(), anyFloat(),
+ eq(transitionRate), anyBoolean());
+ }
+
+ @Test
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1)
public void testRampMaxTimeInteractiveThenIdle() {
// Send a display power request
@@ -1637,13 +1686,20 @@
mock(ScreenOffBrightnessSensorController.class);
final HighBrightnessModeController hbmController = mock(HighBrightnessModeController.class);
final HdrClamper hdrClamper = mock(HdrClamper.class);
+ BrightnessClamperController clamperController = mock(BrightnessClamperController.class);
when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX);
+ when(clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer(
+ invocation -> DisplayBrightnessState.builder()
+ .setIsSlowChange(invocation.getArgument(2))
+ .setBrightness(invocation.getArgument(1))
+ .setMaxBrightness(PowerManager.BRIGHTNESS_MAX)
+ .setCustomAnimationRate(-1).build());
TestInjector injector = spy(new TestInjector(displayPowerState, animator,
automaticBrightnessController, wakelockController, brightnessMappingStrategy,
hysteresisLevels, screenOffBrightnessSensorController, hbmController, hdrClamper,
- flags));
+ clamperController, flags));
final LogicalDisplay display = mock(LogicalDisplay.class);
final DisplayDevice device = mock(DisplayDevice.class);
@@ -1662,8 +1718,8 @@
return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting,
animator, automaticBrightnessController, wakelockController,
- screenOffBrightnessSensorController, hbmController, hdrClamper, hbmMetadata,
- brightnessMappingStrategy, injector, config);
+ screenOffBrightnessSensorController, hbmController, hdrClamper, clamperController,
+ hbmMetadata, brightnessMappingStrategy, injector, config);
}
/**
@@ -1682,6 +1738,7 @@
public final HighBrightnessModeController hbmController;
public final HdrClamper hdrClamper;
+ public final BrightnessClamperController clamperController;
public final HighBrightnessModeMetadata hbmMetadata;
public final BrightnessMappingStrategy brightnessMappingStrategy;
public final DisplayPowerController2.Injector injector;
@@ -1695,6 +1752,7 @@
ScreenOffBrightnessSensorController screenOffBrightnessSensorController,
HighBrightnessModeController hbmController,
HdrClamper hdrClamper,
+ BrightnessClamperController clamperController,
HighBrightnessModeMetadata hbmMetadata,
BrightnessMappingStrategy brightnessMappingStrategy,
DisplayPowerController2.Injector injector,
@@ -1709,6 +1767,7 @@
this.screenOffBrightnessSensorController = screenOffBrightnessSensorController;
this.hbmController = hbmController;
this.hdrClamper = hdrClamper;
+ this.clamperController = clamperController;
this.hbmMetadata = hbmMetadata;
this.brightnessMappingStrategy = brightnessMappingStrategy;
this.injector = injector;
@@ -1728,6 +1787,8 @@
private final HdrClamper mHdrClamper;
+ private final BrightnessClamperController mClamperController;
+
private final DisplayManagerFlags mFlags;
TestInjector(DisplayPowerState dps, DualRampAnimator<DisplayPowerState> animator,
@@ -1738,6 +1799,7 @@
ScreenOffBrightnessSensorController screenOffBrightnessSensorController,
HighBrightnessModeController highBrightnessModeController,
HdrClamper hdrClamper,
+ BrightnessClamperController clamperController,
DisplayManagerFlags flags) {
mDisplayPowerState = dps;
mAnimator = animator;
@@ -1748,6 +1810,7 @@
mScreenOffBrightnessSensorController = screenOffBrightnessSensorController;
mHighBrightnessModeController = highBrightnessModeController;
mHdrClamper = hdrClamper;
+ mClamperController = clamperController;
mFlags = flags;
}
@@ -1864,6 +1927,14 @@
}
@Override
+ BrightnessClamperController getBrightnessClamperController(Handler handler,
+ BrightnessClamperController.ClamperChangeListener clamperChangeListener,
+ BrightnessClamperController.DisplayDeviceData data, Context context,
+ DisplayManagerFlags flags) {
+ return mClamperController;
+ }
+
+ @Override
DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
SensorManager sensorManager, Resources resources) {
return mDisplayWhiteBalanceControllerMock;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
index c0e0df9..ff2b1f4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
@@ -137,8 +137,10 @@
float initialBrightness = 0.8f;
boolean initialSlowChange = true;
float clampedBrightness = 0.6f;
+ float customAnimationRate = 0.01f;
when(mMockClamper.getBrightnessCap()).thenReturn(clampedBrightness);
when(mMockClamper.getType()).thenReturn(BrightnessClamper.Type.THERMAL);
+ when(mMockClamper.getCustomAnimationRate()).thenReturn(customAnimationRate);
when(mMockClamper.isActive()).thenReturn(false);
mTestInjector.mCapturedChangeListener.onChanged();
mTestHandler.flush();
@@ -150,6 +152,7 @@
assertEquals(PowerManager.BRIGHTNESS_MAX, state.getMaxBrightness(), FLOAT_TOLERANCE);
assertEquals(0,
state.getBrightnessReason().getModifier() & BrightnessReason.MODIFIER_THROTTLED);
+ assertEquals(-1, state.getCustomAnimationRate(), FLOAT_TOLERANCE);
assertEquals(initialSlowChange, state.isSlowChange());
}
@@ -158,8 +161,10 @@
float initialBrightness = 0.8f;
boolean initialSlowChange = true;
float clampedBrightness = 0.6f;
+ float customAnimationRate = 0.01f;
when(mMockClamper.getBrightnessCap()).thenReturn(clampedBrightness);
when(mMockClamper.getType()).thenReturn(BrightnessClamper.Type.THERMAL);
+ when(mMockClamper.getCustomAnimationRate()).thenReturn(customAnimationRate);
when(mMockClamper.isActive()).thenReturn(true);
mTestInjector.mCapturedChangeListener.onChanged();
mTestHandler.flush();
@@ -171,6 +176,7 @@
assertEquals(clampedBrightness, state.getMaxBrightness(), FLOAT_TOLERANCE);
assertEquals(BrightnessReason.MODIFIER_THROTTLED,
state.getBrightnessReason().getModifier() & BrightnessReason.MODIFIER_THROTTLED);
+ assertEquals(customAnimationRate, state.getCustomAnimationRate(), FLOAT_TOLERANCE);
assertFalse(state.isSlowChange());
}
@@ -179,8 +185,10 @@
float initialBrightness = 0.6f;
boolean initialSlowChange = true;
float clampedBrightness = 0.8f;
+ float customAnimationRate = 0.01f;
when(mMockClamper.getBrightnessCap()).thenReturn(clampedBrightness);
when(mMockClamper.getType()).thenReturn(BrightnessClamper.Type.THERMAL);
+ when(mMockClamper.getCustomAnimationRate()).thenReturn(customAnimationRate);
when(mMockClamper.isActive()).thenReturn(true);
mTestInjector.mCapturedChangeListener.onChanged();
mTestHandler.flush();
@@ -192,6 +200,7 @@
assertEquals(clampedBrightness, state.getMaxBrightness(), FLOAT_TOLERANCE);
assertEquals(BrightnessReason.MODIFIER_THROTTLED,
state.getBrightnessReason().getModifier() & BrightnessReason.MODIFIER_THROTTLED);
+ assertEquals(customAnimationRate, state.getCustomAnimationRate(), FLOAT_TOLERANCE);
assertFalse(state.isSlowChange());
}
@@ -200,8 +209,10 @@
float initialBrightness = 0.8f;
boolean initialSlowChange = true;
float clampedBrightness = 0.6f;
+ float customAnimationRate = 0.01f;
when(mMockClamper.getBrightnessCap()).thenReturn(clampedBrightness);
when(mMockClamper.getType()).thenReturn(BrightnessClamper.Type.THERMAL);
+ when(mMockClamper.getCustomAnimationRate()).thenReturn(customAnimationRate);
when(mMockClamper.isActive()).thenReturn(true);
mTestInjector.mCapturedChangeListener.onChanged();
mTestHandler.flush();
@@ -216,6 +227,7 @@
assertEquals(clampedBrightness, state.getMaxBrightness(), FLOAT_TOLERANCE);
assertEquals(BrightnessReason.MODIFIER_THROTTLED,
state.getBrightnessReason().getModifier() & BrightnessReason.MODIFIER_THROTTLED);
+ assertEquals(customAnimationRate, state.getCustomAnimationRate(), FLOAT_TOLERANCE);
assertEquals(initialSlowChange, state.isSlowChange());
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
index ee187ba..8d8274c 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
@@ -56,9 +56,9 @@
private static final HdrBrightnessData TEST_HDR_DATA = new HdrBrightnessData(
Map.of(500f, 0.6f),
/* brightnessIncreaseDebounceMillis= */ 1000,
- /* brightnessIncreaseDurationMillis= */ 2000,
+ /* screenBrightnessRampIncrease= */ 0.02f,
/* brightnessDecreaseDebounceMillis= */ 3000,
- /* brightnessDecreaseDurationMillis= */4000
+ /* screenBrightnessRampDecrease= */0.04f
);
private static final int WIDTH = 600;
@@ -152,8 +152,7 @@
mClock.fastForward(3000);
mTestHandler.timeAdvance();
assertEquals(0.6f, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
- // 0.6 to HLG = 0.905727, rate = (1-0.905727) / 4
- assertEquals(0.023568f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
+ assertEquals(0.04, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
}
@Test
@@ -181,8 +180,7 @@
mClock.fastForward(1000);
mTestHandler.timeAdvance();
assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
- // 0.6 to HLG = 0.905727, rate = (1-0.905727) / 2
- assertEquals(0.047137f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
+ assertEquals(0.02f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
}
@Test
@@ -209,8 +207,7 @@
mClock.fastForward(3000);
mTestHandler.timeAdvance();
assertEquals(0.6f, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
- // 0.6 to HLG = 0.905727, rate = (1-0.905727) / 4
- assertEquals(0.023568f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
+ assertEquals(0.04f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
}
// MsgInfo.sendTime is calculated first by adding SystemClock.uptimeMillis()
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
index 596a3f3..b3605cc 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
@@ -280,7 +280,9 @@
0, 0);
// Sleep until timeout should have triggered
- SystemClock.sleep(ActivityManagerService.PROC_START_TIMEOUT + 1000);
+ if (wedge) {
+ SystemClock.sleep(ActivityManagerService.PROC_START_TIMEOUT + 1000);
+ }
return app;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 76b41b7..c493f84 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -2629,7 +2629,7 @@
PROCESS_STATE_NONEXISTENT, PROCESS_STATE_NONEXISTENT,
0, 0, false, false, false, ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE,
false, false, false, hasShownUi, false, false, false, false, false, false, null,
- 0, 0, 0, true, 0, null, false);
+ 0, Long.MIN_VALUE, Long.MIN_VALUE, true, 0, null, false);
}
private ProcessRecord makeProcessRecord(ActivityManagerService service, int pid, int uid,
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 2f6859c..be33b1b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -165,8 +165,7 @@
null
}
whenever(mocks.settings.addPackageLPw(nullable(), nullable(), nullable(), nullable(),
- nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(),
- nullable(), nullable(), nullable(), nullable(), nullable(), nullable())) {
+ nullable(), nullable(), nullable())) {
val name: String = getArgument(0)
val pendingAdd = mPendingPackageAdds.firstOrNull { it.first == name }
?: return@whenever null
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index cf315a4..b9e45ba 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -16,6 +16,7 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
@@ -68,6 +69,7 @@
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.testing.TestableContext;
+import android.util.ArraySet;
import android.view.Display;
import android.view.DisplayAdjustments;
import android.view.DisplayInfo;
@@ -600,6 +602,74 @@
}
@Test
+ public void testPackagesForceStopped_disablesRelevantService() {
+ final AccessibilityServiceInfo info_a = new AccessibilityServiceInfo();
+ info_a.setComponentName(COMPONENT_NAME);
+ final AccessibilityServiceInfo info_b = new AccessibilityServiceInfo();
+ info_b.setComponentName(new ComponentName("package", "class"));
+
+ AccessibilityUserState userState = mA11yms.getCurrentUserState();
+ userState.mInstalledServices.clear();
+ userState.mInstalledServices.add(info_a);
+ userState.mInstalledServices.add(info_b);
+ userState.mEnabledServices.clear();
+ userState.mEnabledServices.add(info_a.getComponentName());
+ userState.mEnabledServices.add(info_b.getComponentName());
+
+ synchronized (mA11yms.getLock()) {
+ mA11yms.onPackagesForceStoppedLocked(
+ new String[]{info_a.getComponentName().getPackageName()}, userState);
+ }
+
+ //Assert user state change
+ userState = mA11yms.getCurrentUserState();
+ assertThat(userState.mEnabledServices).containsExactly(info_b.getComponentName());
+ //Assert setting change
+ final Set<ComponentName> componentsFromSetting = new ArraySet<>();
+ mA11yms.readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ userState.mUserId, componentsFromSetting);
+ assertThat(componentsFromSetting).containsExactly(info_b.getComponentName());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DISABLE_CONTINUOUS_SHORTCUT_ON_FORCE_STOP)
+ public void testPackagesForceStopped_fromContinuousService_removesButtonTarget() {
+ final AccessibilityServiceInfo info_a = new AccessibilityServiceInfo();
+ info_a.setComponentName(COMPONENT_NAME);
+ info_a.flags = FLAG_REQUEST_ACCESSIBILITY_BUTTON;
+ final AccessibilityServiceInfo info_b = new AccessibilityServiceInfo();
+ info_b.setComponentName(new ComponentName("package", "class"));
+
+ AccessibilityUserState userState = mA11yms.getCurrentUserState();
+ userState.mInstalledServices.clear();
+ userState.mInstalledServices.add(info_a);
+ userState.mInstalledServices.add(info_b);
+ userState.mAccessibilityButtonTargets.clear();
+ userState.mAccessibilityButtonTargets.add(info_a.getComponentName().flattenToString());
+ userState.mAccessibilityButtonTargets.add(info_b.getComponentName().flattenToString());
+
+ // despite force stopping both packages, only the first service has the relevant flag,
+ // so only the first should be removed.
+ synchronized (mA11yms.getLock()) {
+ mA11yms.onPackagesForceStoppedLocked(
+ new String[]{
+ info_a.getComponentName().getPackageName(),
+ info_b.getComponentName().getPackageName()},
+ userState);
+ }
+
+ //Assert user state change
+ userState = mA11yms.getCurrentUserState();
+ assertThat(userState.mAccessibilityButtonTargets).containsExactly(
+ info_b.getComponentName().flattenToString());
+ //Assert setting change
+ final Set<String> targetsFromSetting = new ArraySet<>();
+ mA11yms.readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+ userState.mUserId, str -> str, targetsFromSetting);
+ assertThat(targetsFromSetting).containsExactly(info_b.getComponentName().flattenToString());
+ }
+
+ @Test
@RequiresFlagsDisabled(Flags.FLAG_SCAN_PACKAGES_WITHOUT_LOCK)
// Test old behavior to validate lock detection for the old (locked access) case.
public void testPackageMonitorScanPackages_scansWhileHoldingLock() {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index 63281b7..71007f5 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -155,7 +155,7 @@
mUserState.mAccessibilityButtonTargets.add(COMPONENT_NAME.flattenToString());
mUserState.setTargetAssignedToAccessibilityButton(COMPONENT_NAME.flattenToString());
mUserState.setTouchExplorationEnabledLocked(true);
- mUserState.setDisplayMagnificationEnabledLocked(true);
+ mUserState.setMagnificationSingleFingerTripleTapEnabledLocked(true);
mUserState.setAutoclickEnabledLocked(true);
mUserState.setUserNonInteractiveUiTimeoutLocked(30);
mUserState.setUserInteractiveUiTimeoutLocked(30);
@@ -177,7 +177,7 @@
assertTrue(mUserState.mAccessibilityButtonTargets.isEmpty());
assertNull(mUserState.getTargetAssignedToAccessibilityButton());
assertFalse(mUserState.isTouchExplorationEnabledLocked());
- assertFalse(mUserState.isDisplayMagnificationEnabledLocked());
+ assertFalse(mUserState.isMagnificationSingleFingerTripleTapEnabledLocked());
assertFalse(mUserState.isAutoclickEnabledLocked());
assertEquals(0, mUserState.getUserNonInteractiveUiTimeoutLocked());
assertEquals(0, mUserState.getUserInteractiveUiTimeoutLocked());
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 37a6d22..eca19c8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -255,7 +255,7 @@
public void testUnlockUserKeyIfUnsecuredPassesPrimaryUserAuthSecret() throws RemoteException {
initSpAndSetCredential(PRIMARY_USER_ID, newPassword(null));
reset(mAuthSecretService);
- mLocalService.unlockUserKeyIfUnsecured(PRIMARY_USER_ID);
+ mService.unlockUserKeyIfUnsecured(PRIMARY_USER_ID);
verify(mAuthSecretService).setPrimaryUserCredential(any(byte[].class));
}
@@ -267,7 +267,7 @@
mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID);
reset(mAuthSecretService);
- mLocalService.unlockUserKeyIfUnsecured(PRIMARY_USER_ID);
+ mService.unlockUserKeyIfUnsecured(PRIMARY_USER_ID);
verify(mAuthSecretService).setPrimaryUserCredential(any(byte[].class));
}
@@ -285,39 +285,39 @@
@Test
public void testHeadlessSystemUserDoesNotPassAuthSecret() throws RemoteException {
setupHeadlessTest();
- mLocalService.unlockUserKeyIfUnsecured(PRIMARY_USER_ID);
+ mService.unlockUserKeyIfUnsecured(PRIMARY_USER_ID);
verify(mAuthSecretService, never()).setPrimaryUserCredential(any(byte[].class));
}
@Test
public void testHeadlessSecondaryUserPassesAuthSecret() throws RemoteException {
setupHeadlessTest();
- mLocalService.unlockUserKeyIfUnsecured(SECONDARY_USER_ID);
+ mService.unlockUserKeyIfUnsecured(SECONDARY_USER_ID);
verify(mAuthSecretService).setPrimaryUserCredential(any(byte[].class));
}
@Test
public void testHeadlessTertiaryUserPassesSameAuthSecret() throws RemoteException {
setupHeadlessTest();
- mLocalService.unlockUserKeyIfUnsecured(SECONDARY_USER_ID);
+ mService.unlockUserKeyIfUnsecured(SECONDARY_USER_ID);
var captor = ArgumentCaptor.forClass(byte[].class);
verify(mAuthSecretService).setPrimaryUserCredential(captor.capture());
var value = captor.getValue();
reset(mAuthSecretService);
- mLocalService.unlockUserKeyIfUnsecured(TERTIARY_USER_ID);
+ mService.unlockUserKeyIfUnsecured(TERTIARY_USER_ID);
verify(mAuthSecretService).setPrimaryUserCredential(eq(value));
}
@Test
public void testHeadlessTertiaryUserPassesSameAuthSecretAfterReset() throws RemoteException {
setupHeadlessTest();
- mLocalService.unlockUserKeyIfUnsecured(SECONDARY_USER_ID);
+ mService.unlockUserKeyIfUnsecured(SECONDARY_USER_ID);
var captor = ArgumentCaptor.forClass(byte[].class);
verify(mAuthSecretService).setPrimaryUserCredential(captor.capture());
var value = captor.getValue();
mService.clearAuthSecret();
reset(mAuthSecretService);
- mLocalService.unlockUserKeyIfUnsecured(TERTIARY_USER_ID);
+ mService.unlockUserKeyIfUnsecured(TERTIARY_USER_ID);
verify(mAuthSecretService).setPrimaryUserCredential(eq(value));
}
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionTimestampStoreTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionTimestampStoreTest.java
new file mode 100644
index 0000000..7723541
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionTimestampStoreTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media.projection;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.InstantSource;
+
+/**
+ * Tests for the {@link MediaProjectionTimestampStore} class.
+ *
+ * <p>Build/Install/Run: atest FrameworksServicesTests:MediaProjectionTimestampStoreTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class MediaProjectionTimestampStoreTest {
+
+ private static final String TEST_PREFS_FILE = "media-projection-timestamp-test";
+
+ private final Context mContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+ private final File mSharedPreferencesFile = new File(mContext.getCacheDir(), TEST_PREFS_FILE);
+ private final SharedPreferences mSharedPreferences = createSharePreferences();
+
+ private Instant mCurrentInstant = Instant.ofEpochMilli(0);
+
+ private final InstantSource mInstantSource = () -> mCurrentInstant;
+ private final MediaProjectionTimestampStore mStore =
+ new MediaProjectionTimestampStore(mSharedPreferences, mInstantSource);
+
+ @Before
+ public void setUp() {
+ mSharedPreferences.edit().clear().commit();
+ }
+
+ @After
+ public void tearDown() {
+ mSharedPreferences.edit().clear().commit();
+ mSharedPreferencesFile.delete();
+ }
+
+ @Test
+ public void timeSinceLastActiveSession_byDefault_returnsNull() {
+ assertThat(mStore.timeSinceLastActiveSession()).isNull();
+ }
+
+ @Test
+ public void timeSinceLastActiveSession_returnsBasedOnLastActiveSessionEnded() {
+ mCurrentInstant = Instant.ofEpochMilli(0);
+ mStore.registerActiveSessionEnded();
+
+ mCurrentInstant = mCurrentInstant.plusSeconds(60);
+
+ assertThat(mStore.timeSinceLastActiveSession()).isEqualTo(Duration.ofSeconds(60));
+ }
+
+ @Test
+ public void timeSinceLastActiveSession_valueIsPersisted() {
+ mCurrentInstant = Instant.ofEpochMilli(0);
+ mStore.registerActiveSessionEnded();
+
+ MediaProjectionTimestampStore newStoreInstance =
+ new MediaProjectionTimestampStore(createSharePreferences(), mInstantSource);
+ mCurrentInstant = mCurrentInstant.plusSeconds(123);
+
+ assertThat(newStoreInstance.timeSinceLastActiveSession())
+ .isEqualTo(Duration.ofSeconds(123));
+ }
+
+ private SharedPreferences createSharePreferences() {
+ return mContext.getSharedPreferences(mSharedPreferencesFile, Context.MODE_PRIVATE);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index 42be3d3..5d6f36c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -19,7 +19,6 @@
import android.content.pm.SigningDetails;
import android.util.SparseArray;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageUserStateImpl;
@@ -159,17 +158,19 @@
public PackageSetting build() {
final PackageSetting packageSetting = new PackageSetting(mName, mRealName,
- new File(mCodePath), mLegacyNativeLibraryPathString, mPrimaryCpuAbiString,
- mSecondaryCpuAbiString, mCpuAbiOverrideString, mPVersionCode, mPkgFlags,
- mPrivateFlags, mSharedUserId, null /* usesSdkLibraries */,
- null /* usesSdkLibrariesVersions */, null /* usesStaticLibraries */,
- null /* usesStaticLibrariesVersions */, mMimeGroups, mDomainSetId);
- packageSetting.setSignatures(mSigningDetails != null
- ? new PackageSignatures(mSigningDetails)
- : new PackageSignatures());
- packageSetting.setPkg((ParsedPackage) mPkg);
- packageSetting.setAppId(mAppId);
- packageSetting.setVolumeUuid(this.mVolumeUuid);
+ new File(mCodePath), mPkgFlags, mPrivateFlags, mDomainSetId)
+ .setLegacyNativeLibraryPath(mLegacyNativeLibraryPathString)
+ .setPrimaryCpuAbi(mPrimaryCpuAbiString)
+ .setSecondaryCpuAbi(mSecondaryCpuAbiString)
+ .setCpuAbiOverride(mCpuAbiOverrideString)
+ .setLongVersionCode(mPVersionCode)
+ .setSharedUserAppId(mSharedUserId)
+ .setMimeGroups(mMimeGroups)
+ .setSignatures(mSigningDetails != null
+ ? new PackageSignatures(mSigningDetails) : new PackageSignatures())
+ .setPkg(mPkg)
+ .setAppId(mAppId)
+ .setVolumeUuid(this.mVolumeUuid);
if (mInstallSource != null) {
packageSetting.setInstallSource(mInstallSource);
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 3d4b4a6..75d012a 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -7769,6 +7769,74 @@
assertEquals(NotificationManagerService.MAX_PACKAGE_TOASTS, mService.mToastQueue.size());
}
+ @Test
+ public void testPrioritizeSystemToasts() throws Exception {
+ // Insert non-system toasts
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = false;
+ mService.isSystemAppId = false;
+ setToastRateIsWithinQuota(true);
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
+
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, mUserId))
+ .thenReturn(false);
+
+ INotificationManager nmService = (INotificationManager) mService.mService;
+
+ // Enqueue maximum number of toasts for test package
+ for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_TOASTS; i++) {
+ enqueueTextToast(testPackage, "Text");
+ }
+
+ // Enqueue system toast
+ final String testPackageSystem = "testPackageNameSystem";
+ mService.isSystemUid = true;
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackageSystem, false);
+ when(mPackageManager.isPackageSuspendedForUser(testPackageSystem, mUserId))
+ .thenReturn(false);
+
+ enqueueToast(testPackageSystem, new TestableToastCallback());
+
+ // System toast is inserted at the front of the queue, behind current showing toast
+ assertEquals(testPackageSystem, mService.mToastQueue.get(1).pkg);
+ }
+
+ @Test
+ public void testPrioritizeSystemToasts_enqueueAfterExistingSystemToast() throws Exception {
+ // Insert system toasts
+ final String testPackageSystem1 = "testPackageNameSystem1";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = true;
+ setToastRateIsWithinQuota(true);
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackageSystem1, false);
+
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackageSystem1, mUserId))
+ .thenReturn(false);
+
+ INotificationManager nmService = (INotificationManager) mService.mService;
+
+ // Enqueue maximum number of toasts for test package
+ for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_TOASTS; i++) {
+ enqueueTextToast(testPackageSystem1, "Text");
+ }
+
+ // Enqueue another system toast
+ final String testPackageSystem2 = "testPackageNameSystem2";
+ mService.isSystemUid = true;
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackageSystem2, false);
+ when(mPackageManager.isPackageSuspendedForUser(testPackageSystem2, mUserId))
+ .thenReturn(false);
+
+ enqueueToast(testPackageSystem2, new TestableToastCallback());
+
+ // System toast is inserted at the back of the queue, after the other system toasts
+ assertEquals(testPackageSystem2,
+ mService.mToastQueue.get(mService.mToastQueue.size() - 1).pkg);
+ }
+
private void setAppInForegroundForToasts(int uid, boolean inForeground) {
int importance = (inForeground) ? IMPORTANCE_FOREGROUND : IMPORTANCE_NONE;
when(mActivityManager.getUidImportance(mUid)).thenReturn(importance);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
index 121e296..337dd22 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
@@ -91,7 +91,6 @@
private static final Multimap<Class<?>, String> KNOWN_BAD =
ImmutableMultimap.<Class<?>, String>builder()
.put(Person.Builder.class, "setUri") // TODO: b/281044385
- .put(RemoteViews.class, "setRemoteAdapter") // TODO: b/281044385
.build();
// Types that we can't really produce. No methods receiving these parameters will be invoked.
diff --git a/services/tests/vibrator/Android.bp b/services/tests/vibrator/Android.bp
index 9544106..6f37967 100644
--- a/services/tests/vibrator/Android.bp
+++ b/services/tests/vibrator/Android.bp
@@ -35,6 +35,7 @@
"platform-test-annotations",
"service-permission.stubs.system_server",
"services.core",
+ "flag-junit",
],
platform_apis: true,
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
index 0003555..3d0dca0 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
@@ -16,20 +16,24 @@
package com.android.server.vibrator;
+import static android.os.VibrationAttributes.CATEGORY_KEYBOARD;
import static android.os.VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
import static android.os.VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
+import static android.os.VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE;
import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK;
import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK;
+import static android.os.VibrationEffect.EFFECT_CLICK;
import static android.os.VibrationEffect.EFFECT_TEXTURE_TICK;
import static android.os.VibrationEffect.EFFECT_TICK;
import static android.view.HapticFeedbackConstants.CLOCK_TICK;
import static android.view.HapticFeedbackConstants.CONTEXT_CLICK;
+import static android.view.HapticFeedbackConstants.KEYBOARD_RELEASE;
+import static android.view.HapticFeedbackConstants.KEYBOARD_TAP;
import static android.view.HapticFeedbackConstants.SAFE_MODE_ENABLED;
-import static android.view.HapticFeedbackConstants.TEXT_HANDLE_MOVE;
import static android.view.HapticFeedbackConstants.SCROLL_ITEM_FOCUS;
import static android.view.HapticFeedbackConstants.SCROLL_LIMIT;
import static android.view.HapticFeedbackConstants.SCROLL_TICK;
-
+import static android.view.HapticFeedbackConstants.TEXT_HANDLE_MOVE;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -42,9 +46,10 @@
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.VibratorInfo;
+import android.os.vibrator.Flags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.AtomicFile;
import android.util.SparseArray;
-import android.view.flags.FeatureFlags;
import androidx.test.InstrumentationRegistry;
@@ -62,6 +67,8 @@
public class HapticFeedbackVibrationProviderTest {
@Rule public MockitoRule rule = MockitoJUnit.rule();
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private static final VibrationEffect PRIMITIVE_TICK_EFFECT =
VibrationEffect.startComposition().addPrimitive(PRIMITIVE_TICK, 0.2497f).compose();
private static final VibrationEffect PRIMITIVE_CLICK_EFFECT =
@@ -69,11 +76,15 @@
private static final int[] SCROLL_FEEDBACK_CONSTANTS =
new int[] {SCROLL_ITEM_FOCUS, SCROLL_LIMIT, SCROLL_TICK};
+ private static final int[] KEYBOARD_FEEDBACK_CONSTANTS =
+ new int[] {KEYBOARD_TAP, KEYBOARD_RELEASE};
+
+ private static final float KEYBOARD_VIBRATION_FIXED_AMPLITUDE = 0.62f;
+
private Context mContext = InstrumentationRegistry.getContext();
private VibratorInfo mVibratorInfo = VibratorInfo.EMPTY_VIBRATOR_INFO;
@Mock private Resources mResourcesMock;
- @Mock private FeatureFlags mViewFeatureFlags;
@Test
public void testNonExistentCustomization_useDefault() throws Exception {
@@ -214,6 +225,62 @@
}
@Test
+ public void testKeyboardHaptic_noFixedAmplitude_defaultVibrationReturned() {
+ mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
+ SparseArray<VibrationEffect> customizations = new SparseArray<>();
+ customizations.put(KEYBOARD_TAP, PRIMITIVE_CLICK_EFFECT);
+ customizations.put(KEYBOARD_RELEASE, PRIMITIVE_TICK_EFFECT);
+
+ // Test with a customization available for `KEYBOARD_TAP` & `KEYBOARD_RELEASE`.
+ HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations);
+
+ assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_TAP))
+ .isEqualTo(PRIMITIVE_CLICK_EFFECT);
+ assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_RELEASE))
+ .isEqualTo(PRIMITIVE_TICK_EFFECT);
+
+ // Test with no customization available for `KEYBOARD_TAP` & `KEYBOARD_RELEASE`.
+ hapticProvider = createProviderWithDefaultCustomizations();
+
+ assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_TAP))
+ .isEqualTo(VibrationEffect.get(EFFECT_CLICK, true /* fallback */));
+ assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_RELEASE))
+ .isEqualTo(VibrationEffect.get(EFFECT_TICK, false /* fallback */));
+ }
+
+ @Test
+ public void testKeyboardHaptic_fixAmplitude_keyboardCategoryOff_defaultVibrationReturned() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
+ mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
+ mockKeyboardVibrationFixedAmplitude(KEYBOARD_VIBRATION_FIXED_AMPLITUDE);
+
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_TAP))
+ .isEqualTo(VibrationEffect.get(EFFECT_CLICK, true /* fallback */));
+ assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_RELEASE))
+ .isEqualTo(VibrationEffect.get(EFFECT_TICK, false /* fallback */));
+ }
+
+ @Test
+ public void testKeyboardHaptic_fixAmplitude_keyboardCategoryOn_keyboardVibrationReturned() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
+ mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
+ mockKeyboardVibrationFixedAmplitude(KEYBOARD_VIBRATION_FIXED_AMPLITUDE);
+
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_TAP))
+ .isEqualTo(VibrationEffect.startComposition()
+ .addPrimitive(PRIMITIVE_CLICK, KEYBOARD_VIBRATION_FIXED_AMPLITUDE)
+ .compose());
+ assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_RELEASE))
+ .isEqualTo(VibrationEffect.startComposition()
+ .addPrimitive(PRIMITIVE_TICK, KEYBOARD_VIBRATION_FIXED_AMPLITUDE)
+ .compose());
+ }
+
+ @Test
public void testVibrationAttribute_forNotBypassingIntensitySettings() {
HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
@@ -235,7 +302,7 @@
@Test
public void testVibrationAttribute_scrollFeedback_scrollApiFlagOn_bypassInterruptPolicy() {
- when(mViewFeatureFlags.scrollFeedbackApi()).thenReturn(true);
+ mSetFlagsRule.enableFlags(android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API);
HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
for (int effectId : SCROLL_FEEDBACK_CONSTANTS) {
@@ -248,7 +315,7 @@
@Test
public void testVibrationAttribute_scrollFeedback_scrollApiFlagOff_noBypassInterruptPolicy() {
- when(mViewFeatureFlags.scrollFeedbackApi()).thenReturn(false);
+ mSetFlagsRule.disableFlags(android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API);
HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
for (int effectId : SCROLL_FEEDBACK_CONSTANTS) {
@@ -259,14 +326,71 @@
}
}
+ @Test
+ public void testVibrationAttribute_keyboardCategoryOff_notUseKeyboardCategory() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
+ VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+ effectId, /* bypassVibrationIntensitySetting= */ false);
+ assertWithMessage("Expected no CATEGORY_KEYBOARD for effect " + effectId)
+ .that(attrs.getCategory()).isEqualTo(0);
+ }
+ }
+
+ @Test
+ public void testVibrationAttribute_keyboardCategoryOn_useKeyboardCategory() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
+ VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+ effectId, /* bypassVibrationIntensitySetting= */ false);
+ assertWithMessage("Expected CATEGORY_KEYBOARD for effect " + effectId)
+ .that(attrs.getCategory()).isEqualTo(CATEGORY_KEYBOARD);
+ }
+ }
+
+ @Test
+ public void testVibrationAttribute_noFixAmplitude_keyboardCategoryOn_noBypassIntensityScale() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
+ mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
+ mockKeyboardVibrationFixedAmplitude(-1);
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
+ VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+ effectId, /* bypassVibrationIntensitySetting= */ false);
+ assertWithMessage("Expected no FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE for effect "
+ + effectId)
+ .that(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)).isFalse();
+ }
+ }
+
+ @Test
+ public void testVibrationAttribute_fixAmplitude_keyboardCategoryOn_bypassIntensityScale() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
+ mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
+ mockKeyboardVibrationFixedAmplitude(KEYBOARD_VIBRATION_FIXED_AMPLITUDE);
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
+ VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+ effectId, /* bypassVibrationIntensitySetting= */ false);
+ assertWithMessage("Expected FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE for effect "
+ + effectId)
+ .that(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)).isTrue();
+ }
+ }
+
private HapticFeedbackVibrationProvider createProviderWithDefaultCustomizations() {
return createProvider(/* customizations= */ null);
}
private HapticFeedbackVibrationProvider createProvider(
SparseArray<VibrationEffect> customizations) {
- return new HapticFeedbackVibrationProvider(
- mResourcesMock, mVibratorInfo, customizations, mViewFeatureFlags);
+ return new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations);
}
private void mockVibratorPrimitiveSupport(int... supportedPrimitives) {
@@ -287,6 +411,11 @@
.thenReturn(vibrationPattern);
}
+ private void mockKeyboardVibrationFixedAmplitude(float amplitude) {
+ when(mResourcesMock.getFloat(R.dimen.config_keyboardHapticFeedbackFixedAmplitude))
+ .thenReturn(amplitude);
+ }
+
private void setupCustomizationFile(String xml) throws Exception {
File file = new File(mContext.getCacheDir(), "test.xml");
file.createNewFile();
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
index 1ae0966..7a2bb5a 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -69,7 +69,11 @@
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.test.TestLooper;
+import android.os.vibrator.Flags;
import android.os.vibrator.VibrationConfig;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.util.ArraySet;
import android.view.Display;
@@ -95,6 +99,9 @@
public class VibrationSettingsTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
private static final int UID = 1;
private static final int VIRTUAL_DISPLAY_ID = 1;
private static final String SYSUI_PACKAGE_NAME = "sysui";
@@ -606,6 +613,47 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED)
+ public void shouldIgnoreVibration_withKeyboardSettingsOff_shouldIgnoreKeyboardVibration() {
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
+ setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 0);
+
+ // Keyboard touch ignored.
+ assertVibrationIgnoredForAttributes(
+ new VibrationAttributes.Builder()
+ .setUsage(USAGE_TOUCH)
+ .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
+ .build(),
+ Vibration.Status.IGNORED_FOR_SETTINGS);
+
+ // General touch and keyboard touch with bypass flag not ignored.
+ assertVibrationNotIgnoredForUsage(USAGE_TOUCH);
+ assertVibrationNotIgnoredForAttributes(
+ new VibrationAttributes.Builder()
+ .setUsage(USAGE_TOUCH)
+ .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
+ .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
+ .build());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED)
+ public void shouldIgnoreVibration_withKeyboardSettingsOn_shouldNotIgnoreKeyboardVibration() {
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1);
+
+ // General touch ignored.
+ assertVibrationIgnoredForUsage(USAGE_TOUCH, Vibration.Status.IGNORED_FOR_SETTINGS);
+
+ // Keyboard touch not ignored.
+ assertVibrationNotIgnoredForAttributes(
+ new VibrationAttributes.Builder()
+ .setUsage(USAGE_TOUCH)
+ .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
+ .build());
+ }
+
+ @Test
public void shouldIgnoreVibrationFromVirtualDisplays_displayNonVirtual_neverIgnored() {
// Vibrations from the primary display is never ignored regardless of the creation and
// removal of virtual displays and of the changes of apps running on virtual displays.
@@ -895,6 +943,14 @@
mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
+ private void assertVibrationIgnoredForAttributes(VibrationAttributes attrs,
+ Vibration.Status expectedStatus) {
+ Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(attrs, UID,
+ Display.DEFAULT_DISPLAY, null, null);
+ assertEquals(errorMessageForAttributes(attrs), expectedStatus,
+ mVibrationSettings.shouldIgnoreVibration(callerInfo));
+ }
+
private void assertVibrationNotIgnoredForUsage(@VibrationAttributes.Usage int usage) {
assertVibrationNotIgnoredForUsageAndFlags(usage, /* flags= */ 0);
}
@@ -919,10 +975,21 @@
mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
+ private void assertVibrationNotIgnoredForAttributes(VibrationAttributes attrs) {
+ Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(attrs, UID,
+ Display.DEFAULT_DISPLAY, null, null);
+ assertNull(errorMessageForAttributes(attrs),
+ mVibrationSettings.shouldIgnoreVibration(callerInfo));
+ }
+
private String errorMessageForUsage(int usage) {
return "Error for usage " + VibrationAttributes.usageToString(usage);
}
+ private String errorMessageForAttributes(VibrationAttributes attrs) {
+ return "Error for attributes " + attrs;
+ }
+
private void setDefaultIntensity(@Vibrator.VibrationIntensity int intensity) {
when(mVibrationConfigMock.getDefaultVibrationIntensity(anyInt())).thenReturn(intensity);
}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 40e0e84..3dfaed6 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -82,6 +82,7 @@
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -89,7 +90,7 @@
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
-import android.view.flags.FeatureFlags;
+import android.view.flags.Flags;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.FlakyTest;
@@ -155,6 +156,8 @@
@Rule
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Mock
private VibratorManagerService.NativeWrapper mNativeWrapperMock;
@Mock
@@ -175,8 +178,6 @@
private VirtualDeviceManagerInternal mVirtualDeviceManagerInternalMock;
@Mock
private AudioManager mAudioManagerMock;
- @Mock
- private FeatureFlags mViewFeatureFlags;
private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
@@ -326,8 +327,7 @@
HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider(
Resources resources, VibratorInfo vibratorInfo) {
return new HapticFeedbackVibrationProvider(
- resources, vibratorInfo, mHapticFeedbackVibrationMap,
- mViewFeatureFlags);
+ resources, vibratorInfo, mHapticFeedbackVibrationMap);
}
});
return mService;
@@ -1354,7 +1354,7 @@
denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING);
// Flag override to enable the scroll feedack constants to bypass interruption policies.
- when(mViewFeatureFlags.scrollFeedbackApi()).thenReturn(true);
+ mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API);
mHapticFeedbackVibrationMap.put(
HapticFeedbackConstants.SCROLL_TICK,
VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 84d42d42..6a738be 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -163,8 +163,8 @@
public void testUpdateDimsAppliesCrop() {
mHost.addChild(mChild, 0);
- final float alpha = 0.8f;
- mDimmer.dimAbove(mChild, alpha);
+ mDimmer.adjustAppearance(mChild, 1, 1);
+ mDimmer.adjustRelativeLayer(mChild, -1);
int width = 100;
int height = 300;
@@ -176,42 +176,13 @@
}
@Test
- public void testDimAboveWithChildCreatesSurfaceAboveChild_Smooth() {
- assumeTrue(Flags.dimmerRefactor());
- final float alpha = 0.8f;
- mHost.addChild(mChild, 0);
- mDimmer.dimAbove(mChild, alpha);
- SurfaceControl dimLayer = mDimmer.getDimLayer();
-
- assertNotNull("Dimmer should have created a surface", dimLayer);
-
- mDimmer.updateDims(mTransaction);
- verify(sTestAnimation).startAnimation(eq(dimLayer), eq(mTransaction),
- anyInt(), any(SurfaceAnimator.OnAnimationFinishedCallback.class));
- verify(mTransaction).setRelativeLayer(dimLayer, mChild.mControl, 1);
- verify(mTransaction, lastCall()).setAlpha(dimLayer, alpha);
- }
-
- @Test
- public void testDimAboveWithChildCreatesSurfaceAboveChild_Legacy() {
- assumeFalse(Flags.dimmerRefactor());
- final float alpha = 0.8f;
- mHost.addChild(mChild, 0);
- mDimmer.dimAbove(mChild, alpha);
- SurfaceControl dimLayer = mDimmer.getDimLayer();
-
- assertNotNull("Dimmer should have created a surface", dimLayer);
-
- verify(mHost.getPendingTransaction()).setAlpha(dimLayer, alpha);
- verify(mHost.getPendingTransaction()).setRelativeLayer(dimLayer, mChild.mControl, 1);
- }
-
- @Test
public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild_Smooth() {
assumeTrue(Flags.dimmerRefactor());
final float alpha = 0.7f;
+ final int blur = 50;
mHost.addChild(mChild, 0);
- mDimmer.dimBelow(mChild, alpha, 50);
+ mDimmer.adjustAppearance(mChild, alpha, blur);
+ mDimmer.adjustRelativeLayer(mChild, -1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
assertNotNull("Dimmer should have created a surface", dimLayer);
@@ -221,7 +192,7 @@
anyInt(), any(SurfaceAnimator.OnAnimationFinishedCallback.class));
verify(mTransaction).setRelativeLayer(dimLayer, mChild.mControl, -1);
verify(mTransaction, lastCall()).setAlpha(dimLayer, alpha);
- verify(mTransaction).setBackgroundBlurRadius(dimLayer, 50);
+ verify(mTransaction).setBackgroundBlurRadius(dimLayer, blur);
}
@Test
@@ -229,7 +200,8 @@
assumeFalse(Flags.dimmerRefactor());
final float alpha = 0.7f;
mHost.addChild(mChild, 0);
- mDimmer.dimBelow(mChild, alpha, 50);
+ mDimmer.adjustAppearance(mChild, alpha, 20);
+ mDimmer.adjustRelativeLayer(mChild, -1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
assertNotNull("Dimmer should have created a surface", dimLayer);
@@ -244,12 +216,15 @@
mHost.addChild(mChild, 0);
final float alpha = 0.8f;
+ final int blur = 50;
// Dim once
- mDimmer.dimBelow(mChild, alpha, 0);
+ mDimmer.adjustAppearance(mChild, alpha, blur);
+ mDimmer.adjustRelativeLayer(mChild, -1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
mDimmer.updateDims(mTransaction);
// Reset, and don't dim
mDimmer.resetDimStates();
+ mDimmer.adjustRelativeLayer(mChild, -1);
mDimmer.updateDims(mTransaction);
verify(mTransaction).show(dimLayer);
verify(mTransaction).remove(dimLayer);
@@ -261,7 +236,8 @@
mHost.addChild(mChild, 0);
final float alpha = 0.8f;
- mDimmer.dimAbove(mChild, alpha);
+ mDimmer.adjustAppearance(mChild, alpha, 20);
+ mDimmer.adjustRelativeLayer(mChild, -1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
mDimmer.resetDimStates();
@@ -278,13 +254,16 @@
mHost.addChild(mChild, 0);
final float alpha = 0.8f;
+ final int blur = 20;
// Dim once
- mDimmer.dimBelow(mChild, alpha, 0);
+ mDimmer.adjustAppearance(mChild, alpha, blur);
+ mDimmer.adjustRelativeLayer(mChild, -1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
mDimmer.updateDims(mTransaction);
// Reset and dim again
mDimmer.resetDimStates();
- mDimmer.dimAbove(mChild, alpha);
+ mDimmer.adjustAppearance(mChild, alpha, blur);
+ mDimmer.adjustRelativeLayer(mChild, -1);
mDimmer.updateDims(mTransaction);
verify(mTransaction).show(dimLayer);
verify(mTransaction, never()).remove(dimLayer);
@@ -294,7 +273,8 @@
public void testDimUpdateWhileDimming() {
mHost.addChild(mChild, 0);
final float alpha = 0.8f;
- mDimmer.dimAbove(mChild, alpha);
+ mDimmer.adjustAppearance(mChild, alpha, 20);
+ mDimmer.adjustRelativeLayer(mChild, -1);
final Rect bounds = mDimmer.getDimBounds();
SurfaceControl dimLayer = mDimmer.getDimLayer();
@@ -314,7 +294,8 @@
public void testRemoveDimImmediately_Smooth() {
assumeTrue(Flags.dimmerRefactor());
mHost.addChild(mChild, 0);
- mDimmer.dimAbove(mChild, 1);
+ mDimmer.adjustAppearance(mChild, 1, 2);
+ mDimmer.adjustRelativeLayer(mChild, -1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
mDimmer.updateDims(mTransaction);
verify(mTransaction, times(1)).show(dimLayer);
@@ -333,7 +314,8 @@
public void testRemoveDimImmediately_Legacy() {
assumeFalse(Flags.dimmerRefactor());
mHost.addChild(mChild, 0);
- mDimmer.dimAbove(mChild, 1);
+ mDimmer.adjustAppearance(mChild, 1, 0);
+ mDimmer.adjustRelativeLayer(mChild, -1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
mDimmer.updateDims(mTransaction);
verify(mTransaction, times(1)).show(dimLayer);
@@ -351,16 +333,16 @@
@Test
public void testDimmerWithBlurUpdatesTransaction_Legacy() {
assumeFalse(Flags.dimmerRefactor());
- TestWindowContainer child = new TestWindowContainer(mWm);
- mHost.addChild(child, 0);
+ mHost.addChild(mChild, 0);
final int blurRadius = 50;
- mDimmer.dimBelow(child, 0, blurRadius);
+ mDimmer.adjustAppearance(mChild, 1, blurRadius);
+ mDimmer.adjustRelativeLayer(mChild, -1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
assertNotNull("Dimmer should have created a surface", dimLayer);
verify(mHost.getPendingTransaction()).setBackgroundBlurRadius(dimLayer, blurRadius);
- verify(mHost.getPendingTransaction()).setRelativeLayer(dimLayer, child.mControl, -1);
+ verify(mHost.getPendingTransaction()).setRelativeLayer(dimLayer, mChild.mControl, -1);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
index 75a8dd8..085eddd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.platform.test.annotations.Presubmit;
@@ -94,10 +95,14 @@
public void testRemoveFinishingInvisibleActivityFromUnknown() {
final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity);
- activity.finishing = true;
- activity.setVisibleRequested(true);
- activity.setVisibility(false);
+ assertFalse(mDisplayContent.mUnknownAppVisibilityController.allResolved());
+ activity.makeFinishingLocked();
assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
+
+ mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity);
+ assertTrue(mDisplayContent.mUnknownAppVisibilityController.isVisibilityUnknown(activity));
+ activity.setState(ActivityRecord.State.STOPPED, "test");
+ assertFalse(mDisplayContent.mUnknownAppVisibilityController.isVisibilityUnknown(activity));
}
@Test
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
index 42b08e3..93b5a40 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
@@ -20,6 +20,8 @@
import static android.Manifest.permission.LOG_COMPAT_CHANGE;
import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
import static android.Manifest.permission.RECORD_AUDIO;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.service.attention.AttentionService.PROXIMITY_UNKNOWN;
import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_EXTERNAL;
import static android.service.voice.HotwordDetectionService.ENABLE_PROXIMITY_RESULT;
@@ -753,11 +755,21 @@
"Failed to obtain permission RECORD_AUDIO for identity "
+ mVoiceInteractorIdentity);
}
- mAppOpsManager.noteOpNoThrow(
- AppOpsPolicy.getVoiceActivationOp(),
- mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
- mVoiceInteractorIdentity.attributionTag,
- HOTWORD_DETECTION_OP_MESSAGE);
+ int opMode = mAppOpsManager.unsafeCheckOpNoThrow(
+ AppOpsManager.opToPublicName(AppOpsPolicy.getVoiceActivationOp()),
+ mVoiceInteractorIdentity.uid,
+ mVoiceInteractorIdentity.packageName);
+ if (opMode == MODE_DEFAULT || opMode == MODE_ALLOWED) {
+ mAppOpsManager.noteOpNoThrow(
+ AppOpsPolicy.getVoiceActivationOp(),
+ mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
+ mVoiceInteractorIdentity.attributionTag,
+ HOTWORD_DETECTION_OP_MESSAGE);
+ } else {
+ throw new SecurityException(
+ "The app op OP_RECEIVE_SANDBOX_TRIGGER_AUDIO is denied for "
+ + "identity" + mVoiceInteractorIdentity);
+ }
} else {
enforcePermissionForDataDelivery(mContext, mVoiceInteractorIdentity,
RECORD_AUDIO, HOTWORD_DETECTION_OP_MESSAGE);
diff --git a/telephony/java/android/telephony/satellite/INtnSignalStrengthCallback.aidl b/telephony/java/android/telephony/satellite/INtnSignalStrengthCallback.aidl
new file mode 100644
index 0000000..54cab48
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/INtnSignalStrengthCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+import android.telephony.satellite.NtnSignalStrength;
+
+/**
+ * Interface for non-terrestrial signal strength notify callback.
+ * @hide
+ */
+oneway interface INtnSignalStrengthCallback {
+ /**
+ * Called when NTN signal strength changes.
+ * @param ntnSignalStrength The new NTN signal strength.
+ */
+ void onNtnSignalStrengthChanged(in NtnSignalStrength ntnSignalStrength);
+}
diff --git a/telephony/java/android/telephony/satellite/NtnSignalStrength.aidl b/telephony/java/android/telephony/satellite/NtnSignalStrength.aidl
new file mode 100644
index 0000000..a79cb69
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/NtnSignalStrength.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+parcelable NtnSignalStrength;
diff --git a/telephony/java/android/telephony/satellite/NtnSignalStrength.java b/telephony/java/android/telephony/satellite/NtnSignalStrength.java
new file mode 100644
index 0000000..16d7654
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/NtnSignalStrength.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * NTN signal strength related information.
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public final class NtnSignalStrength implements Parcelable {
+ /** Non-terrestrial network signal strength is not available. */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public static final int NTN_SIGNAL_STRENGTH_NONE = 0;
+ /** Non-terrestrial network signal strength is poor. */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public static final int NTN_SIGNAL_STRENGTH_POOR = 1;
+ /** Non-terrestrial network signal strength is moderate. */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public static final int NTN_SIGNAL_STRENGTH_MODERATE = 2;
+ /** Non-terrestrial network signal strength is good. */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public static final int NTN_SIGNAL_STRENGTH_GOOD = 3;
+ /** Non-terrestrial network signal strength is great. */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public static final int NTN_SIGNAL_STRENGTH_GREAT = 4;
+ @NtnSignalStrengthLevel private int mLevel;
+
+ /** @hide */
+ @IntDef(prefix = "NTN_SIGNAL_STRENGTH_", value = {
+ NTN_SIGNAL_STRENGTH_NONE,
+ NTN_SIGNAL_STRENGTH_POOR,
+ NTN_SIGNAL_STRENGTH_MODERATE,
+ NTN_SIGNAL_STRENGTH_GOOD,
+ NTN_SIGNAL_STRENGTH_GREAT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NtnSignalStrengthLevel {}
+
+ /**
+ * Create a parcelable object to inform the current non-terrestrial signal strength
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public NtnSignalStrength(@NtnSignalStrengthLevel int level) {
+ this.mLevel = level;
+ }
+
+ /**
+ * This constructor is used to create a copy of an existing NtnSignalStrength object.
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public NtnSignalStrength(@Nullable NtnSignalStrength source) {
+ this.mLevel = (source == null) ? NTN_SIGNAL_STRENGTH_NONE : source.getLevel();
+ }
+
+ private NtnSignalStrength(Parcel in) {
+ readFromParcel(in);
+ }
+
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @NtnSignalStrengthLevel public int getLevel() {
+ return mLevel;
+ }
+
+ /**
+ * @return 0
+ */
+ @Override
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * @param out The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
+ */
+ @Override
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeInt(mLevel);
+ }
+
+ private void readFromParcel(Parcel in) {
+ mLevel = in.readInt();
+ }
+
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @NonNull public static final Creator<NtnSignalStrength> CREATOR =
+ new Creator<NtnSignalStrength>() {
+ @Override public NtnSignalStrength createFromParcel(Parcel in) {
+ return new NtnSignalStrength(in);
+ }
+
+ @Override public NtnSignalStrength[] newArray(int size) {
+ return new NtnSignalStrength[size];
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ return mLevel;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null || getClass() != obj.getClass()) return false;
+
+ NtnSignalStrength that = (NtnSignalStrength) obj;
+ return mLevel == that.mLevel;
+ }
+
+ @Override public String toString() {
+ return "NtnSignalStrength{"
+ + "mLevel=" + mLevel
+ + '}';
+ }
+}
diff --git a/telephony/java/android/telephony/satellite/NtnSignalStrengthCallback.java b/telephony/java/android/telephony/satellite/NtnSignalStrengthCallback.java
new file mode 100644
index 0000000..4b79590
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/NtnSignalStrengthCallback.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.telephony.flags.Flags;
+
+/**
+ * A callback class for notifying satellite signal strength change.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public interface NtnSignalStrengthCallback {
+ /**
+ * Called when non-terrestrial network signal strength changes.
+ * @param ntnSignalStrength The new non-terrestrial network signal strength.
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ void onNtnSignalStrengthChanged(@NonNull NtnSignalStrength ntnSignalStrength);
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 7322aeb..21d93bd 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -35,7 +35,9 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.TelephonyManager;
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.ITelephony;
@@ -77,6 +79,8 @@
private static final ConcurrentHashMap<SatelliteTransmissionUpdateCallback,
ISatelliteTransmissionUpdateCallback> sSatelliteTransmissionUpdateCallbackMap =
new ConcurrentHashMap<>();
+ private static final ConcurrentHashMap<NtnSignalStrengthCallback, INtnSignalStrengthCallback>
+ sNtnSignalStrengthCallbackMap = new ConcurrentHashMap<>();
private final int mSubId;
@@ -192,6 +196,14 @@
public static final String KEY_SATELLITE_NEXT_VISIBILITY = "satellite_next_visibility";
/**
+ * Bundle key to get the response from
+ * {@link #requestNtnSignalStrength(Executor, OutcomeReceiver)}.
+ * @hide
+ */
+
+ public static final String KEY_NTN_SIGNAL_STRENGTH = "ntn_signal_strength";
+
+ /**
* The request was successfully processed.
*/
@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
@@ -1866,6 +1878,165 @@
return new HashSet<>();
}
+ /**
+ * Request to get the signal strength of the satellite connection.
+ *
+ * <p>
+ * Note: This API is specifically designed for OEM enabled satellite connectivity only.
+ * For satellite connectivity enabled using carrier roaming, please refer to
+ * {@link android.telephony.TelephonyCallback.SignalStrengthsListener}, and
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * </p>
+ *
+ * @param executor The executor on which the callback will be called.
+ * @param callback The callback object to which the result will be delivered. If the request is
+ * successful, {@link OutcomeReceiver#onResult(Object)} will return an instance of
+ * {@link NtnSignalStrength} with a value of {@link NtnSignalStrength.NtnSignalStrengthLevel}
+ * The {@link NtnSignalStrength#NTN_SIGNAL_STRENGTH_NONE} will be returned if there is no
+ * signal strength data available.
+ * If the request is not successful, {@link OutcomeReceiver#onError(Throwable)} will return a
+ * {@link SatelliteException} with the {@link SatelliteResult}.
+ *
+ * @throws SecurityException if the caller doesn't have required permission.
+ * @throws IllegalStateException if the Telephony process is not currently available or
+ * satellite is not supported.
+ */
+ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @NonNull
+ public void requestNtnSignalStrength(@NonNull @CallbackExecutor Executor executor,
+ @NonNull OutcomeReceiver<NtnSignalStrength, SatelliteException> callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ ResultReceiver receiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (resultCode == SATELLITE_RESULT_SUCCESS) {
+ if (resultData.containsKey(KEY_NTN_SIGNAL_STRENGTH)) {
+ NtnSignalStrength ntnSignalStrength =
+ resultData.getParcelable(KEY_NTN_SIGNAL_STRENGTH,
+ NtnSignalStrength.class);
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onResult(ntnSignalStrength)));
+ } else {
+ loge("KEY_NTN_SIGNAL_STRENGTH does not exist.");
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onError(new SatelliteException(
+ SATELLITE_RESULT_REQUEST_FAILED))));
+ }
+ } else {
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onError(new SatelliteException(resultCode))));
+ }
+ }
+ };
+ telephony.requestNtnSignalStrength(mSubId, receiver);
+ } else {
+ throw new IllegalStateException("Telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ loge("requestNtnSignalStrength() RemoteException: " + ex);
+ ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Registers for NTN signal strength changed from satellite modem.
+ *
+ * <p>
+ * Note: This API is specifically designed for OEM enabled satellite connectivity only.
+ * For satellite connectivity enabled using carrier roaming, please refer to
+ * {@link android.telephony.TelephonyCallback.SignalStrengthsListener}, and
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * </p>
+ *
+ * @param executor The executor on which the callback will be called.
+ * @param callback The callback to handle the NTN signal strength changed event.
+ *
+ * @return The {@link SatelliteResult} result of the operation.
+ *
+ * @throws SecurityException if the caller doesn't have required permission.
+ * @throws IllegalStateException if the Telephony process is not currently available.
+ */
+ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @SatelliteResult public int registerForNtnSignalStrengthChanged(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull NtnSignalStrengthCallback callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ INtnSignalStrengthCallback internalCallback =
+ new INtnSignalStrengthCallback.Stub() {
+ @Override
+ public void onNtnSignalStrengthChanged(
+ NtnSignalStrength ntnSignalStrength) {
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> callback.onNtnSignalStrengthChanged(
+ ntnSignalStrength)));
+ }
+ };
+ sNtnSignalStrengthCallbackMap.put(callback, internalCallback);
+ return telephony.registerForNtnSignalStrengthChanged(mSubId, internalCallback);
+ } else {
+ throw new IllegalStateException("Telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ loge("registerForNtnSignalStrengthChanged() RemoteException: " + ex);
+ ex.rethrowFromSystemServer();
+ }
+ return SATELLITE_RESULT_REQUEST_FAILED;
+ }
+
+ /**
+ * Unregisters for NTN signal strength changed from satellite modem.
+ * If callback was not registered before, the request will be ignored.
+ *
+ * <p>
+ * Note: This API is specifically designed for OEM enabled satellite connectivity only.
+ * For satellite connectivity enabled using carrier roaming, please refer to
+ * {@link TelephonyManager#unregisterTelephonyCallback(TelephonyCallback)}..
+ * </p>
+ *
+ * @param callback The callback that was passed to
+ * {@link #registerForNtnSignalStrengthChanged(Executor, NtnSignalStrengthCallback)}.
+ *
+ * @throws SecurityException if the caller doesn't have required permission.
+ * @throws IllegalStateException if the Telephony process is not currently available.
+ */
+ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public void unregisterForNtnSignalStrengthChanged(@NonNull NtnSignalStrengthCallback callback) {
+ Objects.requireNonNull(callback);
+ INtnSignalStrengthCallback internalCallback =
+ sNtnSignalStrengthCallbackMap.remove(callback);
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ if (internalCallback != null) {
+ telephony.unregisterForNtnSignalStrengthChanged(mSubId, internalCallback);
+ } else {
+ loge("unregisterForNtnSignalStrengthChanged: No internal callback.");
+ }
+ } else {
+ throw new IllegalStateException("Telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ loge("unregisterForNtnSignalStrengthChanged() RemoteException: " + ex);
+ ex.rethrowFromSystemServer();
+ }
+
+ }
+
+
private static ITelephony getITelephony() {
ITelephony binder = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer
.getTelephonyServiceManager()
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt b/telephony/java/android/telephony/satellite/stub/INtnSignalStrengthConsumer.aidl
similarity index 64%
copy from packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt
copy to telephony/java/android/telephony/satellite/stub/INtnSignalStrengthConsumer.aidl
index 0803a01..b7712bd 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt
+++ b/telephony/java/android/telephony/satellite/stub/INtnSignalStrengthConsumer.aidl
@@ -12,15 +12,17 @@
* 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.communal.shared
+package android.telephony.satellite.stub;
-import android.appwidget.AppWidgetProviderInfo
+import android.telephony.satellite.NtnSignalStrength;
-/** A data class that stores info about an app widget that displays in communal mode. */
-data class CommunalAppWidgetInfo(
- val providerInfo: AppWidgetProviderInfo,
- val appWidgetId: Int,
-)
+/**
+ * Consumer pattern for a request that receives the signal strength of non-terrestrial network from
+ * the SatelliteService.
+ * @hide
+ */
+oneway interface INtnSignalStrengthConsumer {
+ void accept(in NtnSignalStrength result);
+}
diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
index 7fda550..6b47db1 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
@@ -16,6 +16,7 @@
package android.telephony.satellite.stub;
+import android.telephony.satellite.stub.INtnSignalStrengthConsumer;
import android.telephony.satellite.stub.ISatelliteCapabilitiesConsumer;
import android.telephony.satellite.stub.ISatelliteListener;
import android.telephony.satellite.stub.SatelliteDatagram;
@@ -454,4 +455,44 @@
*/
void requestIsSatelliteEnabledForCarrier(int simSlot, in IIntegerConsumer resultCallback,
in IBooleanConsumer callback);
+
+ /**
+ * Request to get the signal strength of the satellite connection.
+ *
+ * @param resultCallback The {@link SatelliteError} result of the operation.
+ * @param callback The callback to handle the NTN signal strength changed event.
+ */
+ void requestSignalStrength(in IIntegerConsumer resultCallback,
+ in INtnSignalStrengthConsumer callback);
+
+ /**
+ * The satellite service should report the NTN signal strength via
+ * ISatelliteListener#onNtnSignalStrengthChanged when the NTN signal strength changes.
+ *
+ * @param resultCallback The callback to receive the error code result of the operation.
+ *
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ */
+ void startSendingNtnSignalStrength(in IIntegerConsumer resultCallback);
+
+ /**
+ * The satellite service should stop reporting NTN signal strength to the framework. This will
+ * be called when device is screen off to save power by not letting signal strength updates to
+ * wake up application processor.
+ *
+ * @param resultCallback The callback to receive the error code result of the operation.
+ *
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ */
+ void stopSendingNtnSignalStrength(in IIntegerConsumer resultCallback);
}
diff --git a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
index d687162..d44ddfa 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
@@ -16,6 +16,7 @@
package android.telephony.satellite.stub;
+import android.telephony.satellite.stub.NtnSignalStrength;
import android.telephony.satellite.stub.NTRadioTechnology;
import android.telephony.satellite.stub.PointingInfo;
import android.telephony.satellite.stub.SatelliteDatagram;
@@ -58,4 +59,10 @@
* @param state The current satellite modem state.
*/
void onSatelliteModemStateChanged(in SatelliteModemState state);
+
+ /**
+ * Called when NTN signal strength changes.
+ * @param ntnSignalStrength The new NTN signal strength.
+ */
+ void onNtnSignalStrengthChanged(in NtnSignalStrength ntnSignalStrength);
}
diff --git a/telephony/java/android/telephony/satellite/stub/NtnSignalStrength.aidl b/telephony/java/android/telephony/satellite/stub/NtnSignalStrength.aidl
new file mode 100644
index 0000000..f489005
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/stub/NtnSignalStrength.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite.stub;
+
+import android.telephony.satellite.stub.NtnSignalStrengthLevel;
+
+/**
+ * @hide
+ */
+parcelable NtnSignalStrength {
+ /**
+ * Non-terrestrial signal strength. The value represents the level of signal strength which can
+ * be translated to the number of signal bars.
+ */
+ NtnSignalStrengthLevel signalStrengthLevel;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt b/telephony/java/android/telephony/satellite/stub/NtnSignalStrengthLevel.aidl
similarity index 67%
copy from packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt
copy to telephony/java/android/telephony/satellite/stub/NtnSignalStrengthLevel.aidl
index 0803a01..53b1373 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalAppWidgetInfo.kt
+++ b/telephony/java/android/telephony/satellite/stub/NtnSignalStrengthLevel.aidl
@@ -12,15 +12,18 @@
* 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.communal.shared
+package android.telephony.satellite.stub;
-import android.appwidget.AppWidgetProviderInfo
-
-/** A data class that stores info about an app widget that displays in communal mode. */
-data class CommunalAppWidgetInfo(
- val providerInfo: AppWidgetProviderInfo,
- val appWidgetId: Int,
-)
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum NtnSignalStrengthLevel {
+ NTN_SIGNAL_STRENGTH_NONE = 0,
+ NTN_SIGNAL_STRENGTH_POOR = 1,
+ NTN_SIGNAL_STRENGTH_MODERATE = 2,
+ NTN_SIGNAL_STRENGTH_GOOD = 3,
+ NTN_SIGNAL_STRENGTH_GREAT = 4
+}
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
index 4cee01e..a636a61 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
@@ -241,6 +241,30 @@
"requestIsSatelliteEnabledForCarrier");
}
+ @Override
+ public void requestSignalStrength(IIntegerConsumer resultCallback,
+ INtnSignalStrengthConsumer callback) throws RemoteException {
+ executeMethodAsync(
+ () -> SatelliteImplBase.this.requestSignalStrength(resultCallback, callback),
+ "requestSignalStrength");
+ }
+
+ @Override
+ public void startSendingNtnSignalStrength(IIntegerConsumer resultCallback)
+ throws RemoteException {
+ executeMethodAsync(
+ () -> SatelliteImplBase.this.startSendingNtnSignalStrength(resultCallback),
+ "startSendingNtnSignalStrength");
+ }
+
+ @Override
+ public void stopSendingNtnSignalStrength(IIntegerConsumer resultCallback)
+ throws RemoteException {
+ executeMethodAsync(
+ () -> SatelliteImplBase.this.stopSendingNtnSignalStrength(resultCallback),
+ "stopSendingNtnSignalStrength");
+ }
+
// Call the methods with a clean calling identity on the executor and wait indefinitely for
// the future to return.
private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
@@ -728,4 +752,35 @@
@NonNull IIntegerConsumer resultCallback, @NonNull IBooleanConsumer callback) {
// stub implementation
}
+
+ /**
+ * Request to get the signal strength of the satellite connection.
+ *
+ * @param resultCallback The {@link SatelliteError} result of the operation.
+ * @param callback The callback to handle the NTN signal strength changed event.
+ */
+ public void requestSignalStrength(@NonNull IIntegerConsumer resultCallback,
+ INtnSignalStrengthConsumer callback) {
+ // stub implementation
+ }
+
+ /**
+ * Requests to deliver signal strength changed events through the
+ * {@link ISatelliteListener#onNtnSignalStrengthChanged(NtnSignalStrength ntnSignalStrength)}
+ * callback.
+ *
+ * @param resultCallback The {@link SatelliteError} result of the operation.
+ */
+ public void startSendingNtnSignalStrength(@NonNull IIntegerConsumer resultCallback) {
+ // stub implementation
+ }
+
+ /**
+ * Requests to stop signal strength changed events
+ *
+ * @param resultCallback The {@link SatelliteError} result of the operation.
+ */
+ public void stopSendingNtnSignalStrength(@NonNull IIntegerConsumer resultCallback){
+ // stub implementation
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 3aa5a5a..58e7026 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -67,10 +67,12 @@
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.aidl.IRcsConfigCallback;
+import android.telephony.satellite.INtnSignalStrengthCallback;
import android.telephony.satellite.ISatelliteDatagramCallback;
import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
import android.telephony.satellite.ISatelliteProvisionStateCallback;
import android.telephony.satellite.ISatelliteStateCallback;
+import android.telephony.satellite.NtnSignalStrength;
import android.telephony.satellite.SatelliteCapabilities;
import android.telephony.satellite.SatelliteDatagram;
import com.android.ims.internal.IImsServiceFeatureCallback;
@@ -2837,7 +2839,6 @@
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
void deprovisionSatelliteService(int subId, in String token, in IIntegerConsumer callback);
-
/**
* Registers for provision state changed from satellite modem.
*
@@ -3071,4 +3072,40 @@
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
int[] getSatelliteAttachRestrictionReasonsForCarrier(int subId);
+
+ /**
+ * Request to get the signal strength of the satellite connection.
+ *
+ * @param subId The subId of the subscription to request for.
+ * @param receiver Result receiver to get the error code of the request and the current signal
+ * strength of the satellite connection.
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+ void requestNtnSignalStrength(int subId, in ResultReceiver receiver);
+
+ /**
+ * Registers for NTN signal strength changed from satellite modem.
+ *
+ * @param subId The subId of the subscription to request for.
+ * @param callback The callback to handle the NTN signal strength changed event.
+ *
+ * @return The {@link SatelliteResult} result of the operation.
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+ int registerForNtnSignalStrengthChanged(int subId, in INtnSignalStrengthCallback callback);
+
+ /**
+ * Unregisters for NTN signal strength changed from satellite modem.
+ * If callback was not registered before, the request will be ignored.
+ *
+ * @param subId The subId of the subscription to unregister for provision state changed.
+ * @param callback The callback that was passed to
+ * {@link #registerForNtnSignalStrengthChanged(Executor, NtnSignalStrengthCallback)}.
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+ void unregisterForNtnSignalStrengthChanged(int subId,
+ in INtnSignalStrengthCallback callback);
}
diff --git a/tests/FsVerityTest/AndroidTest.xml b/tests/FsVerityTest/AndroidTest.xml
index 49cbde0..d2537f6 100644
--- a/tests/FsVerityTest/AndroidTest.xml
+++ b/tests/FsVerityTest/AndroidTest.xml
@@ -24,7 +24,7 @@
<!-- This test requires root to write against block device. -->
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
- <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="test-file-name" value="FsVerityTestApp.apk"/>
<option name="cleanup-apks" value="true"/>
</target_preparer>
diff --git a/tools/aapt2/integration-tests/AutoVersionTest/Android.bp b/tools/aapt2/integration-tests/AutoVersionTest/Android.bp
index bfd3508..c901efa 100644
--- a/tools/aapt2/integration-tests/AutoVersionTest/Android.bp
+++ b/tools/aapt2/integration-tests/AutoVersionTest/Android.bp
@@ -26,4 +26,5 @@
android_test {
name: "AaptAutoVersionTest",
sdk_version: "current",
+ use_resource_processor: false,
}
diff --git a/tools/aapt2/integration-tests/BasicTest/Android.bp b/tools/aapt2/integration-tests/BasicTest/Android.bp
index 7db9d26..d0649ea 100644
--- a/tools/aapt2/integration-tests/BasicTest/Android.bp
+++ b/tools/aapt2/integration-tests/BasicTest/Android.bp
@@ -26,4 +26,5 @@
android_test {
name: "AaptBasicTest",
sdk_version: "current",
+ use_resource_processor: false,
}
diff --git a/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp
index 80404ee..ebb4e9f 100644
--- a/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp
@@ -24,9 +24,9 @@
}
android_test {
-
name: "AaptTestStaticLib_App",
sdk_version: "current",
+ use_resource_processor: false,
srcs: ["src/**/*.java"],
asset_dirs: [
"assets",
diff --git a/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp
index a84da43..ee12a929 100644
--- a/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp
@@ -26,6 +26,7 @@
android_library {
name: "AaptTestStaticLib_LibOne",
sdk_version: "current",
+ use_resource_processor: false,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
}
diff --git a/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp
index d386c3a..83b23624 100644
--- a/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp
@@ -26,6 +26,7 @@
android_library {
name: "AaptTestStaticLib_LibTwo",
sdk_version: "current",
+ use_resource_processor: false,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
libs: ["AaptTestStaticLib_LibOne"],
diff --git a/tools/aapt2/integration-tests/SymlinkTest/Android.bp b/tools/aapt2/integration-tests/SymlinkTest/Android.bp
index 1e8cf86..15a6a20 100644
--- a/tools/aapt2/integration-tests/SymlinkTest/Android.bp
+++ b/tools/aapt2/integration-tests/SymlinkTest/Android.bp
@@ -26,4 +26,5 @@
android_test {
name: "AaptSymlinkTest",
sdk_version: "current",
+ use_resource_processor: false,
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
index 6bf074b..523106f 100755
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
@@ -36,7 +36,7 @@
HOSTSTUBGEN=hoststubgen
# Rebuild the tool and the dependencies. These are the only things we build with the build system.
-run m $HOSTSTUBGEN hoststubgen-annotations hoststubgen-helper-runtime truth-prebuilt junit
+run m $HOSTSTUBGEN hoststubgen-annotations hoststubgen-helper-runtime truth junit
# Build tiny-framework
@@ -55,7 +55,7 @@
test_compile_classpaths=(
$SOONG_INT/external/junit/junit/android_common/combined/junit.jar
- $ANDROID_HOST_OUT/framework/truth-prebuilt.jar
+ $SOONG_INT/external/truth/truth/android_common/combined/truth.jar
)
test_runtime_classpaths=(
diff --git a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
index 2a199d2..58638e8 100644
--- a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -113,6 +113,7 @@
private HashMap<String, IPnoScanEvent> mPnoScanEventHandlers = new HashMap<>();
private HashMap<String, IApInterfaceEventCallback> mApInterfaceListeners = new HashMap<>();
private Runnable mDeathEventHandler;
+ private Object mLock = new Object();
/**
* Ensures that no more than one sendMgmtFrame operation runs concurrently.
*/
@@ -625,13 +626,15 @@
@VisibleForTesting
public void binderDied() {
mEventHandler.post(() -> {
- Log.e(TAG, "Wificond died!");
- clearState();
- // Invalidate the global wificond handle on death. Will be refreshed
- // on the next setup call.
- mWificond = null;
- if (mDeathEventHandler != null) {
- mDeathEventHandler.run();
+ synchronized (mLock) {
+ Log.e(TAG, "Wificond died!");
+ clearState();
+ // Invalidate the global wificond handle on death. Will be refreshed
+ // on the next setup call.
+ mWificond = null;
+ if (mDeathEventHandler != null) {
+ mDeathEventHandler.run();
+ }
}
});
}
@@ -867,26 +870,28 @@
* @return Returns true on success.
*/
public boolean tearDownInterfaces() {
- Log.d(TAG, "tearing down interfaces in wificond");
- // Explicitly refresh the wificodn handler because |tearDownInterfaces()|
- // could be used to cleanup before we setup any interfaces.
- if (!retrieveWificondAndRegisterForDeath()) {
+ synchronized (mLock) {
+ Log.d(TAG, "tearing down interfaces in wificond");
+ // Explicitly refresh the wificond handler because |tearDownInterfaces()|
+ // could be used to cleanup before we setup any interfaces.
+ if (!retrieveWificondAndRegisterForDeath()) {
+ return false;
+ }
+
+ try {
+ for (Map.Entry<String, IWifiScannerImpl> entry : mWificondScanners.entrySet()) {
+ entry.getValue().unsubscribeScanEvents();
+ entry.getValue().unsubscribePnoScanEvents();
+ }
+ mWificond.tearDownInterfaces();
+ clearState();
+ return true;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to tear down interfaces due to remote exception");
+ }
+
return false;
}
-
- try {
- for (Map.Entry<String, IWifiScannerImpl> entry : mWificondScanners.entrySet()) {
- entry.getValue().unsubscribeScanEvents();
- entry.getValue().unsubscribePnoScanEvents();
- }
- mWificond.tearDownInterfaces();
- clearState();
- return true;
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to tear down interfaces due to remote exception");
- }
-
- return false;
}
/** Helper function to look up the interface handle using name */
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java
index 01f1591..3d5a0f7 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java
@@ -16,6 +16,7 @@
package android.net.wifi.sharedconnectivity.app;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -169,6 +170,7 @@
* @return Returns the Builder object.
*/
@NonNull
+ @FlaggedApi("com.android.wifi.flags.network_provider_battery_charging_status")
public Builder setBatteryCharging(boolean isBatteryCharging) {
mIsBatteryCharging = isBatteryCharging;
return this;
@@ -283,6 +285,7 @@
*
* @return Returns true if the battery of the remote device is charging.
*/
+ @FlaggedApi("com.android.wifi.flags.network_provider_battery_charging_status")
public boolean isBatteryCharging() {
return mIsBatteryCharging;
}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
index 71ac94b..b0f68f7 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -17,6 +17,7 @@
package android.net.wifi.sharedconnectivity.app;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -296,6 +297,7 @@
*/
@TestApi
@NonNull
+ @FlaggedApi("com.android.wifi.flags.shared_connectivity_broadcast_receiver_test_api")
public BroadcastReceiver getBroadcastReceiver() {
return mBroadcastReceiver;
}